Coverage Report

Created: 2026-09-14 06:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/librabbitmq/librabbitmq/amqp_socket.c
Line
Count
Source
1
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
2
// SPDX-License-Identifier: mit
3
4
#ifdef HAVE_CONFIG_H
5
#include "config.h"
6
#endif
7
8
#ifdef _MSC_VER
9
#define _CRT_SECURE_NO_WARNINGS
10
#endif
11
12
#include "amqp_private.h"
13
#include "amqp_socket.h"
14
#include "amqp_table.h"
15
#include "amqp_time.h"
16
17
#include <assert.h>
18
#include <limits.h>
19
#include <stdarg.h>
20
#include <stdint.h>
21
#include <stdio.h>
22
#include <stdlib.h>
23
#include <string.h>
24
25
#include <errno.h>
26
27
#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__)))
28
#ifndef WIN32_LEAN_AND_MEAN
29
#define WIN32_LEAN_AND_MEAN
30
#endif
31
#include <winsock2.h>
32
#include <ws2tcpip.h>
33
#else
34
#include <sys/types.h>
35
/* On older BSD types.h must come before net includes */
36
#include <netinet/in.h>
37
#include <netinet/tcp.h>
38
#ifdef HAVE_SELECT
39
#include <sys/select.h>
40
#endif
41
#include <fcntl.h>
42
#include <netdb.h>
43
#include <sys/socket.h>
44
#include <sys/uio.h>
45
#ifdef HAVE_POLL
46
#include <poll.h>
47
#endif
48
#include <unistd.h>
49
#endif
50
51
static int amqp_id_in_reply_list(amqp_method_number_t expected,
52
                                 amqp_method_number_t *list);
53
54
0
static int amqp_os_socket_init(void) {
55
#ifdef _WIN32
56
  static int called_wsastartup = 0;
57
  if (!called_wsastartup) {
58
    WSADATA data;
59
    int res = WSAStartup(0x0202, &data);
60
    if (res) {
61
      return AMQP_STATUS_TCP_SOCKETLIB_INIT_ERROR;
62
    }
63
64
    called_wsastartup = 1;
65
  }
66
  return AMQP_STATUS_OK;
67
68
#else
69
0
  return AMQP_STATUS_OK;
70
0
#endif
71
0
}
72
73
0
int amqp_os_socket_error(void) {
74
#ifdef _WIN32
75
  return WSAGetLastError();
76
#else
77
0
  return errno;
78
0
#endif
79
0
}
80
81
0
int amqp_os_socket_close(int sockfd) {
82
#ifdef _WIN32
83
  return closesocket(sockfd);
84
#else
85
0
  return close(sockfd);
86
0
#endif
87
0
}
88
89
ssize_t amqp_socket_send(amqp_socket_t *self, const void *buf, size_t len,
90
0
                         int flags) {
91
0
  assert(self);
92
0
  assert(self->klass->send);
93
0
  return self->klass->send(self, buf, len, flags);
94
0
}
95
96
ssize_t amqp_socket_recv(amqp_socket_t *self, void *buf, size_t len,
97
0
                         int flags) {
98
0
  assert(self);
99
0
  assert(self->klass->recv);
100
0
  return self->klass->recv(self, buf, len, flags);
101
0
}
102
103
0
int amqp_socket_open(amqp_socket_t *self, const char *host, int port) {
104
0
  assert(self);
105
0
  assert(self->klass->open);
106
0
  return self->klass->open(self, host, port, NULL);
107
0
}
108
109
int amqp_socket_open_noblock(amqp_socket_t *self, const char *host, int port,
110
0
                             const struct timeval *timeout) {
111
0
  assert(self);
112
0
  assert(self->klass->open);
113
0
  return self->klass->open(self, host, port, timeout);
114
0
}
115
116
0
int amqp_socket_close(amqp_socket_t *self, amqp_socket_close_enum force) {
117
0
  assert(self);
118
0
  assert(self->klass->close);
119
0
  return self->klass->close(self, force);
120
0
}
121
122
0
void amqp_socket_delete(amqp_socket_t *self) {
123
0
  if (self) {
124
0
    assert(self->klass->delete);
125
0
    self->klass->delete (self);
126
0
  }
127
0
}
128
129
0
int amqp_socket_get_sockfd(amqp_socket_t *self) {
130
0
  assert(self);
131
0
  assert(self->klass->get_sockfd);
132
0
  return self->klass->get_sockfd(self);
133
0
}
134
135
0
int amqp_poll(int fd, int event, amqp_time_t deadline) {
136
0
#ifdef HAVE_POLL
137
0
  struct pollfd pfd;
138
0
  int res;
139
0
  int timeout_ms;
140
141
  /* Function should only ever be called with one of these two */
142
0
  assert(event == AMQP_SF_POLLIN || event == AMQP_SF_POLLOUT);
143
144
0
start_poll:
145
0
  pfd.fd = fd;
146
0
  switch (event) {
147
0
    case AMQP_SF_POLLIN:
148
0
      pfd.events = POLLIN;
149
0
      break;
150
0
    case AMQP_SF_POLLOUT:
151
0
      pfd.events = POLLOUT;
152
0
      break;
153
0
  }
154
155
0
  timeout_ms = amqp_time_ms_until(deadline);
156
0
  if (-1 > timeout_ms) {
157
0
    return timeout_ms;
158
0
  }
159
160
0
  res = poll(&pfd, 1, timeout_ms);
161
162
0
  if (0 < res) {
163
    /* TODO: optimize this a bit by returning the AMQP_STATUS_SOCKET_ERROR or
164
     * equivalent when pdf.revent is POLLHUP or POLLERR, so an extra syscall
165
     * doesn't need to be made. */
166
0
    return AMQP_STATUS_OK;
167
0
  } else if (0 == res) {
168
0
    return AMQP_STATUS_TIMEOUT;
169
0
  } else {
170
0
    switch (amqp_os_socket_error()) {
171
0
      case EINTR:
172
0
        goto start_poll;
173
0
      default:
174
0
        return AMQP_STATUS_SOCKET_ERROR;
175
0
    }
176
0
  }
177
#elif defined(HAVE_SELECT)
178
  fd_set fds;
179
  fd_set exceptfds;
180
  fd_set *exceptfdsp;
181
  int res;
182
  struct timeval tv;
183
  struct timeval *tvp;
184
185
  assert((0 != (event & AMQP_SF_POLLIN)) || (0 != (event & AMQP_SF_POLLOUT)));
186
#ifndef _WIN32
187
  /* On Win32 connect() failure is indicated through the exceptfds, it does not
188
   * make any sense to allow POLLERR on any other platform or condition */
189
  assert(0 == (event & AMQP_SF_POLLERR));
190
#endif
191
192
start_select:
193
  FD_ZERO(&fds);
194
  FD_SET(fd, &fds);
195
196
  if (event & AMQP_SF_POLLERR) {
197
    FD_ZERO(&exceptfds);
198
    FD_SET(fd, &exceptfds);
199
    exceptfdsp = &exceptfds;
200
  } else {
201
    exceptfdsp = NULL;
202
  }
203
204
  res = amqp_time_tv_until(deadline, &tv, &tvp);
205
  if (res != AMQP_STATUS_OK) {
206
    return res;
207
  }
208
209
  if (event & AMQP_SF_POLLIN) {
210
    res = select(fd + 1, &fds, NULL, exceptfdsp, tvp);
211
  } else if (event & AMQP_SF_POLLOUT) {
212
    res = select(fd + 1, NULL, &fds, exceptfdsp, tvp);
213
  }
214
215
  if (0 < res) {
216
    return AMQP_STATUS_OK;
217
  } else if (0 == res) {
218
    return AMQP_STATUS_TIMEOUT;
219
  } else {
220
    switch (amqp_os_socket_error()) {
221
      case EINTR:
222
        goto start_select;
223
      default:
224
        return AMQP_STATUS_SOCKET_ERROR;
225
    }
226
  }
227
#else
228
#error "poll() or select() is needed to compile rabbitmq-c"
229
#endif
230
0
}
231
232
static ssize_t do_poll(amqp_connection_state_t state, ssize_t res,
233
0
                       amqp_time_t deadline) {
234
0
  int fd = amqp_get_sockfd(state);
235
0
  if (-1 == fd) {
236
0
    return AMQP_STATUS_SOCKET_CLOSED;
237
0
  }
238
0
  switch (res) {
239
0
    case AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD:
240
0
      res = amqp_poll(fd, AMQP_SF_POLLIN, deadline);
241
0
      break;
242
0
    case AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE:
243
0
      res = amqp_poll(fd, AMQP_SF_POLLOUT, deadline);
244
0
      break;
245
0
  }
246
0
  return res;
247
0
}
248
249
ssize_t amqp_try_send(amqp_connection_state_t state, const void *buf,
250
0
                      size_t len, amqp_time_t deadline, int flags) {
251
0
  ssize_t res;
252
0
  void *buf_left = (void *)buf;
253
  /* Assume that len is not going to be larger than ssize_t can hold. */
254
0
  ssize_t len_left = (size_t)len;
255
256
0
start_send:
257
0
  res = amqp_socket_send(state->socket, buf_left, len_left, flags);
258
259
0
  if (res > 0) {
260
0
    len_left -= res;
261
0
    buf_left = (char *)buf_left + res;
262
0
    if (0 == len_left) {
263
0
      return (ssize_t)len;
264
0
    }
265
0
    goto start_send;
266
0
  }
267
0
  res = do_poll(state, res, deadline);
268
0
  if (AMQP_STATUS_OK == res) {
269
0
    goto start_send;
270
0
  }
271
0
  if (AMQP_STATUS_TIMEOUT == res) {
272
0
    return (ssize_t)len - len_left;
273
0
  }
274
0
  return res;
275
0
}
276
277
0
int amqp_open_socket(char const *hostname, int portnumber) {
278
0
  return amqp_open_socket_inner(hostname, portnumber, amqp_time_infinite());
279
0
}
280
281
int amqp_open_socket_noblock(char const *hostname, int portnumber,
282
0
                             const struct timeval *timeout) {
283
0
  amqp_time_t deadline;
284
0
  int res = amqp_time_from_now(&deadline, timeout);
285
0
  if (AMQP_STATUS_OK != res) {
286
0
    return res;
287
0
  }
288
0
  return amqp_open_socket_inner(hostname, portnumber, deadline);
289
0
}
290
291
#ifdef _WIN32
292
static int connect_socket(struct addrinfo *addr, amqp_time_t deadline) {
293
  int one = 1;
294
  u_long nonblocking = 1;
295
  SOCKET sockfd;
296
  int last_error;
297
298
  /*
299
   * This cast is to squash warnings on Win64, see:
300
   * http://stackoverflow.com/questions/1953639/is-it-safe-to-cast-socket-to-int-under-win64
301
   */
302
303
  sockfd = (int)socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
304
  if (INVALID_SOCKET == sockfd) {
305
    return AMQP_STATUS_SOCKET_ERROR;
306
  }
307
308
  /* Set the socket to be non-blocking */
309
  if (SOCKET_ERROR == ioctlsocket(sockfd, FIONBIO, &nonblocking)) {
310
    last_error = AMQP_STATUS_SOCKET_ERROR;
311
    goto err;
312
  }
313
314
  /* Disable nagle */
315
  if (SOCKET_ERROR == setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,
316
                                 (const char *)&one, sizeof(one))) {
317
    last_error = AMQP_STATUS_SOCKET_ERROR;
318
    goto err;
319
  }
320
321
  /* Enable TCP keepalives */
322
  if (SOCKET_ERROR == setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
323
                                 (const char *)&one, sizeof(one))) {
324
    last_error = AMQP_STATUS_SOCKET_ERROR;
325
    goto err;
326
  }
327
328
  if (SOCKET_ERROR != connect(sockfd, addr->ai_addr, (int)addr->ai_addrlen)) {
329
    return (int)sockfd;
330
  }
331
332
  if (WSAEWOULDBLOCK != WSAGetLastError()) {
333
    last_error = AMQP_STATUS_SOCKET_ERROR;
334
    goto err;
335
  }
336
337
  last_error =
338
      amqp_poll((int)sockfd, AMQP_SF_POLLOUT | AMQP_SF_POLLERR, deadline);
339
  if (AMQP_STATUS_OK != last_error) {
340
    goto err;
341
  }
342
343
  {
344
    int result;
345
    int result_len = sizeof(result);
346
347
    if (SOCKET_ERROR == getsockopt(sockfd, SOL_SOCKET, SO_ERROR,
348
                                   (char *)&result, &result_len) ||
349
        result != 0) {
350
      last_error = AMQP_STATUS_SOCKET_ERROR;
351
      goto err;
352
    }
353
  }
354
355
  return (int)sockfd;
356
357
err:
358
  closesocket(sockfd);
359
  return last_error;
360
}
361
#else
362
0
static int connect_socket(struct addrinfo *addr, amqp_time_t deadline) {
363
0
  int one = 1;
364
0
  int sockfd;
365
0
  int flags;
366
0
  int last_error;
367
368
0
  sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
369
0
  if (-1 == sockfd) {
370
0
    return AMQP_STATUS_SOCKET_ERROR;
371
0
  }
372
373
  /* Enable CLOEXEC on socket */
374
0
  flags = fcntl(sockfd, F_GETFD);
375
0
  if (flags == -1 || fcntl(sockfd, F_SETFD, (long)(flags | FD_CLOEXEC)) == -1) {
376
0
    last_error = AMQP_STATUS_SOCKET_ERROR;
377
0
    goto err;
378
0
  }
379
380
  /* Set the socket as non-blocking */
381
0
  flags = fcntl(sockfd, F_GETFL);
382
0
  if (flags == -1 || fcntl(sockfd, F_SETFL, (long)(flags | O_NONBLOCK)) == -1) {
383
0
    last_error = AMQP_STATUS_SOCKET_ERROR;
384
0
    goto err;
385
0
  }
386
387
#ifdef SO_NOSIGPIPE
388
  /* Turn off SIGPIPE on platforms that support it, BSD, MacOSX */
389
  if (0 != setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one))) {
390
    last_error = AMQP_STATUS_SOCKET_ERROR;
391
    goto err;
392
  }
393
#endif /* SO_NOSIGPIPE */
394
395
  /* Disable nagle */
396
0
  if (0 != setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one))) {
397
0
    last_error = AMQP_STATUS_SOCKET_ERROR;
398
0
    goto err;
399
0
  }
400
401
  /* Enable TCP keepalives */
402
0
  if (0 != setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one))) {
403
0
    last_error = AMQP_STATUS_SOCKET_ERROR;
404
0
    goto err;
405
0
  }
406
407
0
  if (0 == connect(sockfd, addr->ai_addr, addr->ai_addrlen)) {
408
0
    return sockfd;
409
0
  }
410
411
0
  if (EINPROGRESS != errno) {
412
0
    last_error = AMQP_STATUS_SOCKET_ERROR;
413
0
    goto err;
414
0
  }
415
416
0
  last_error = amqp_poll(sockfd, AMQP_SF_POLLOUT, deadline);
417
0
  if (AMQP_STATUS_OK != last_error) {
418
0
    goto err;
419
0
  }
420
421
0
  {
422
0
    int result;
423
0
    socklen_t result_len = sizeof(result);
424
425
0
    if (-1 == getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &result, &result_len) ||
426
0
        result != 0) {
427
0
      last_error = AMQP_STATUS_SOCKET_ERROR;
428
0
      goto err;
429
0
    }
430
0
  }
431
432
0
  return sockfd;
433
434
0
err:
435
0
  close(sockfd);
436
0
  return last_error;
437
0
}
438
#endif
439
440
int amqp_open_socket_inner(char const *hostname, int portnumber,
441
0
                           amqp_time_t deadline) {
442
0
  struct addrinfo hint;
443
0
  struct addrinfo *address_list;
444
0
  struct addrinfo *addr;
445
0
  char portnumber_string[33];
446
0
  int sockfd = -1;
447
0
  int last_error;
448
449
0
  last_error = amqp_os_socket_init();
450
0
  if (AMQP_STATUS_OK != last_error) {
451
0
    return last_error;
452
0
  }
453
454
0
  memset(&hint, 0, sizeof(hint));
455
0
  hint.ai_family = PF_UNSPEC; /* PF_INET or PF_INET6 */
456
0
  hint.ai_socktype = SOCK_STREAM;
457
0
  hint.ai_protocol = IPPROTO_TCP;
458
459
0
  (void)sprintf(portnumber_string, "%d", portnumber);
460
461
0
  last_error = getaddrinfo(hostname, portnumber_string, &hint, &address_list);
462
0
  if (0 != last_error) {
463
0
    return AMQP_STATUS_HOSTNAME_RESOLUTION_FAILED;
464
0
  }
465
466
0
  for (addr = address_list; addr; addr = addr->ai_next) {
467
0
    sockfd = connect_socket(addr, deadline);
468
469
0
    if (sockfd >= 0) {
470
0
      last_error = AMQP_STATUS_OK;
471
0
      break;
472
0
    } else if (sockfd == AMQP_STATUS_TIMEOUT) {
473
0
      last_error = sockfd;
474
0
      break;
475
0
    }
476
0
  }
477
478
0
  freeaddrinfo(address_list);
479
0
  if (last_error != AMQP_STATUS_OK || sockfd == -1) {
480
0
    return last_error;
481
0
  }
482
0
  return sockfd;
483
0
}
484
485
static int send_header_inner(amqp_connection_state_t state,
486
0
                             amqp_time_t deadline) {
487
0
  ssize_t res;
488
0
  static const uint8_t header[8] = {'A',
489
0
                                    'M',
490
0
                                    'Q',
491
0
                                    'P',
492
0
                                    0,
493
0
                                    AMQP_PROTOCOL_VERSION_MAJOR,
494
0
                                    AMQP_PROTOCOL_VERSION_MINOR,
495
0
                                    AMQP_PROTOCOL_VERSION_REVISION};
496
0
  res = amqp_try_send(state, header, sizeof(header), deadline, AMQP_SF_NONE);
497
0
  if (sizeof(header) == res) {
498
0
    return AMQP_STATUS_OK;
499
0
  }
500
0
  return (int)res;
501
0
}
502
503
0
int amqp_send_header(amqp_connection_state_t state) {
504
0
  return send_header_inner(state, amqp_time_infinite());
505
0
}
506
507
0
static amqp_bytes_t sasl_method_name(amqp_sasl_method_enum method) {
508
0
  amqp_bytes_t res;
509
510
0
  switch (method) {
511
0
    case AMQP_SASL_METHOD_PLAIN:
512
0
      res = amqp_literal_bytes("PLAIN");
513
0
      break;
514
0
    case AMQP_SASL_METHOD_EXTERNAL:
515
0
      res = amqp_literal_bytes("EXTERNAL");
516
0
      break;
517
518
0
    default:
519
0
      amqp_abort("Invalid SASL method: %d", (int)method);
520
0
  }
521
522
0
  return res;
523
0
}
524
525
0
static int bytes_equal(amqp_bytes_t l, amqp_bytes_t r) {
526
0
  if (l.len == r.len) {
527
0
    if (l.bytes && r.bytes) {
528
0
      if (0 == memcmp(l.bytes, r.bytes, l.len)) {
529
0
        return 1;
530
0
      }
531
0
    }
532
0
  }
533
0
  return 0;
534
0
}
535
536
int sasl_mechanism_in_list(amqp_bytes_t mechanisms,
537
0
                           amqp_sasl_method_enum method) {
538
0
  amqp_bytes_t mechanism;
539
0
  amqp_bytes_t supported_mechanism;
540
0
  uint8_t *start;
541
0
  uint8_t *end;
542
0
  uint8_t *current;
543
544
0
  assert(NULL != mechanisms.bytes);
545
546
0
  mechanism = sasl_method_name(method);
547
548
0
  start = (uint8_t *)mechanisms.bytes;
549
0
  current = start;
550
0
  end = start + mechanisms.len;
551
552
0
  for (; current != end; start = current + 1) {
553
    /* HACK: SASL states that we should be parsing this string as a UTF-8
554
     * string, which we're plainly not doing here. At this point its not worth
555
     * dragging an entire UTF-8 parser for this one case, and this should work
556
     * most of the time */
557
0
    current = memchr(start, ' ', end - start);
558
0
    if (NULL == current) {
559
0
      current = end;
560
0
    }
561
0
    supported_mechanism.bytes = start;
562
0
    supported_mechanism.len = current - start;
563
0
    if (bytes_equal(mechanism, supported_mechanism)) {
564
0
      return 1;
565
0
    }
566
0
  }
567
568
0
  return 0;
569
0
}
570
571
static amqp_bytes_t sasl_response(amqp_pool_t *pool,
572
                                  amqp_sasl_method_enum method, va_list args,
573
0
                                  int *status) {
574
0
  amqp_bytes_t response = amqp_empty_bytes;
575
576
0
  *status = AMQP_STATUS_OK;
577
578
0
  switch (method) {
579
0
    case AMQP_SASL_METHOD_PLAIN: {
580
0
      char *username = va_arg(args, char *);
581
0
      char *password = va_arg(args, char *);
582
0
      size_t username_len;
583
0
      size_t password_len;
584
0
      char *response_buf;
585
586
0
      if (username == NULL || password == NULL) {
587
0
        *status = AMQP_STATUS_INVALID_PARAMETER;
588
0
        return response;
589
0
      }
590
591
0
      username_len = strlen(username);
592
0
      password_len = strlen(password);
593
594
0
      amqp_pool_alloc_bytes(pool, username_len + password_len + 2, &response);
595
0
      if (response.bytes == NULL)
596
      /* We never request a zero-length block, because of the +2
597
         above, so a NULL here really is ENOMEM. */
598
0
      {
599
0
        *status = AMQP_STATUS_NO_MEMORY;
600
0
        return response;
601
0
      }
602
603
0
      response_buf = response.bytes;
604
0
      response_buf[0] = 0;
605
0
      memcpy(response_buf + 1, username, username_len);
606
0
      response_buf[username_len + 1] = 0;
607
0
      memcpy(response_buf + username_len + 2, password, password_len);
608
0
      break;
609
0
    }
610
0
    case AMQP_SASL_METHOD_EXTERNAL: {
611
0
      char *identity = va_arg(args, char *);
612
0
      size_t identity_len;
613
614
      /* NULL is treated the same as an empty identity: the caller has no
615
         authorization identity to send and the broker should rely on the
616
         identity established by the underlying transport (e.g. the TLS
617
         client certificate). See RFC 4422 Appendix A. */
618
0
      if (identity == NULL) {
619
0
        identity = "";
620
0
      }
621
622
0
      identity_len = strlen(identity);
623
0
      if (identity_len == 0) {
624
        /* response is already amqp_empty_bytes; amqp_pool_alloc_bytes()
625
           would return NULL for a zero-length request, which is
626
           indistinguishable from ENOMEM, so skip the allocation entirely. */
627
0
        break;
628
0
      }
629
630
0
      amqp_pool_alloc_bytes(pool, identity_len, &response);
631
0
      if (response.bytes == NULL) {
632
0
        *status = AMQP_STATUS_NO_MEMORY;
633
0
        return response;
634
0
      }
635
636
0
      memcpy(response.bytes, identity, identity_len);
637
0
      break;
638
0
    }
639
0
    default:
640
0
      amqp_abort("Invalid SASL method: %d", (int)method);
641
0
  }
642
643
0
  return response;
644
0
}
645
646
0
amqp_boolean_t amqp_frames_enqueued(amqp_connection_state_t state) {
647
0
  return (state->first_queued_frame != NULL);
648
0
}
649
650
/*
651
 * Check to see if we have data in our buffer. If this returns 1, we
652
 * will avoid an immediate blocking read in amqp_simple_wait_frame.
653
 */
654
0
amqp_boolean_t amqp_data_in_buffer(amqp_connection_state_t state) {
655
0
  return (state->sock_inbound_offset < state->sock_inbound_limit);
656
0
}
657
658
static int consume_one_frame(amqp_connection_state_t state,
659
0
                             amqp_frame_t *decoded_frame) {
660
0
  int res;
661
662
0
  amqp_bytes_t buffer;
663
0
  buffer.len = state->sock_inbound_limit - state->sock_inbound_offset;
664
0
  buffer.bytes =
665
0
      ((char *)state->sock_inbound_buffer.bytes) + state->sock_inbound_offset;
666
667
0
  res = amqp_handle_input(state, buffer, decoded_frame);
668
0
  if (res < 0) {
669
0
    return res;
670
0
  }
671
672
0
  state->sock_inbound_offset += res;
673
674
0
  return AMQP_STATUS_OK;
675
0
}
676
677
static int recv_with_timeout(amqp_connection_state_t state,
678
0
                             amqp_time_t timeout) {
679
0
  ssize_t res;
680
0
  int fd;
681
682
0
start_recv:
683
0
  res = amqp_socket_recv(state->socket, state->sock_inbound_buffer.bytes,
684
0
                         state->sock_inbound_buffer.len, 0);
685
686
0
  if (res < 0) {
687
0
    fd = amqp_get_sockfd(state);
688
0
    if (-1 == fd) {
689
0
      return AMQP_STATUS_CONNECTION_CLOSED;
690
0
    }
691
0
    switch (res) {
692
0
      default:
693
0
        return (int)res;
694
0
      case AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD:
695
0
        res = amqp_poll(fd, AMQP_SF_POLLIN, timeout);
696
0
        break;
697
0
      case AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE:
698
0
        res = amqp_poll(fd, AMQP_SF_POLLOUT, timeout);
699
0
        break;
700
0
    }
701
0
    if (AMQP_STATUS_OK == res) {
702
0
      goto start_recv;
703
0
    }
704
0
    return (int)res;
705
0
  }
706
707
0
  state->sock_inbound_limit = res;
708
0
  state->sock_inbound_offset = 0;
709
710
0
  res = amqp_time_s_from_now(&state->next_recv_heartbeat,
711
0
                             amqp_heartbeat_recv(state));
712
0
  if (AMQP_STATUS_OK != res) {
713
0
    return (int)res;
714
0
  }
715
0
  return AMQP_STATUS_OK;
716
0
}
717
718
0
int amqp_try_recv(amqp_connection_state_t state) {
719
0
  amqp_time_t timeout;
720
0
  int res;
721
722
0
  while (amqp_data_in_buffer(state)) {
723
0
    amqp_frame_t frame;
724
0
    res = consume_one_frame(state, &frame);
725
726
0
    if (AMQP_STATUS_OK != res) {
727
0
      return res;
728
0
    }
729
730
0
    if (frame.frame_type != 0) {
731
0
      amqp_pool_t *channel_pool;
732
0
      amqp_frame_t *frame_copy;
733
0
      amqp_link_t *link;
734
735
0
      channel_pool = amqp_get_or_create_channel_pool(state, frame.channel);
736
0
      if (NULL == channel_pool) {
737
0
        return AMQP_STATUS_NO_MEMORY;
738
0
      }
739
740
0
      frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t));
741
0
      link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t));
742
743
0
      if (frame_copy == NULL || link == NULL) {
744
0
        return AMQP_STATUS_NO_MEMORY;
745
0
      }
746
747
0
      *frame_copy = frame;
748
749
0
      link->next = NULL;
750
0
      link->data = frame_copy;
751
752
0
      if (state->last_queued_frame == NULL) {
753
0
        state->first_queued_frame = link;
754
0
      } else {
755
0
        state->last_queued_frame->next = link;
756
0
      }
757
0
      state->last_queued_frame = link;
758
0
    }
759
0
  }
760
0
  res = amqp_time_from_now(&timeout, &(struct timeval){0});
761
0
  if (AMQP_STATUS_OK != res) {
762
0
    return res;
763
0
  }
764
765
0
  return recv_with_timeout(state, timeout);
766
0
}
767
768
static int wait_frame_inner(amqp_connection_state_t state,
769
                            amqp_frame_t *decoded_frame,
770
0
                            amqp_time_t timeout_deadline) {
771
0
  amqp_time_t deadline;
772
0
  int res;
773
774
0
  for (;;) {
775
0
    while (amqp_data_in_buffer(state)) {
776
0
      res = consume_one_frame(state, decoded_frame);
777
778
0
      if (AMQP_STATUS_OK != res) {
779
0
        return res;
780
0
      }
781
782
0
      if (AMQP_FRAME_HEARTBEAT == decoded_frame->frame_type) {
783
0
        amqp_maybe_release_buffers_on_channel(state, 0);
784
0
        continue;
785
0
      }
786
787
0
      if (decoded_frame->frame_type != 0) {
788
        /* Complete frame was read. Return it. */
789
0
        return AMQP_STATUS_OK;
790
0
      }
791
0
    }
792
793
0
  beginrecv:
794
0
    res = amqp_time_has_past(state->next_send_heartbeat);
795
0
    if (AMQP_STATUS_TIMER_FAILURE == res) {
796
0
      return res;
797
0
    } else if (AMQP_STATUS_TIMEOUT == res) {
798
0
      amqp_frame_t heartbeat;
799
0
      heartbeat.channel = 0;
800
0
      heartbeat.frame_type = AMQP_FRAME_HEARTBEAT;
801
802
0
      res = amqp_send_frame(state, &heartbeat);
803
0
      if (AMQP_STATUS_OK != res) {
804
0
        return res;
805
0
      }
806
0
    }
807
0
    deadline = amqp_time_first(timeout_deadline,
808
0
                               amqp_time_first(state->next_recv_heartbeat,
809
0
                                               state->next_send_heartbeat));
810
811
    /* TODO this needs to wait for a _frame_ and not anything written from the
812
     * socket */
813
0
    res = recv_with_timeout(state, deadline);
814
815
0
    if (AMQP_STATUS_TIMEOUT == res) {
816
0
      if (amqp_time_equal(deadline, state->next_recv_heartbeat)) {
817
0
        amqp_socket_close(state->socket, AMQP_SC_FORCE);
818
0
        return AMQP_STATUS_HEARTBEAT_TIMEOUT;
819
0
      } else if (amqp_time_equal(deadline, timeout_deadline)) {
820
0
        return AMQP_STATUS_TIMEOUT;
821
0
      } else if (amqp_time_equal(deadline, state->next_send_heartbeat)) {
822
        /* send heartbeat happens before we do recv_with_timeout */
823
0
        goto beginrecv;
824
0
      } else {
825
0
        amqp_abort("Internal error: unable to determine timeout reason");
826
0
      }
827
0
    } else if (AMQP_STATUS_OK != res) {
828
0
      return res;
829
0
    }
830
0
  }
831
0
}
832
833
static amqp_link_t *amqp_create_link_for_frame(amqp_connection_state_t state,
834
0
                                               amqp_frame_t *frame) {
835
0
  amqp_link_t *link;
836
0
  amqp_frame_t *frame_copy;
837
838
0
  amqp_pool_t *channel_pool =
839
0
      amqp_get_or_create_channel_pool(state, frame->channel);
840
841
0
  if (NULL == channel_pool) {
842
0
    return NULL;
843
0
  }
844
845
0
  link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t));
846
0
  frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t));
847
848
0
  if (NULL == link || NULL == frame_copy) {
849
0
    return NULL;
850
0
  }
851
852
0
  *frame_copy = *frame;
853
0
  link->data = frame_copy;
854
855
0
  return link;
856
0
}
857
858
0
int amqp_queue_frame(amqp_connection_state_t state, amqp_frame_t *frame) {
859
0
  amqp_link_t *link = amqp_create_link_for_frame(state, frame);
860
0
  if (NULL == link) {
861
0
    return AMQP_STATUS_NO_MEMORY;
862
0
  }
863
864
0
  if (NULL == state->first_queued_frame) {
865
0
    state->first_queued_frame = link;
866
0
  } else {
867
0
    state->last_queued_frame->next = link;
868
0
  }
869
870
0
  link->next = NULL;
871
0
  state->last_queued_frame = link;
872
873
0
  return AMQP_STATUS_OK;
874
0
}
875
876
0
int amqp_put_back_frame(amqp_connection_state_t state, amqp_frame_t *frame) {
877
0
  amqp_link_t *link = amqp_create_link_for_frame(state, frame);
878
0
  if (NULL == link) {
879
0
    return AMQP_STATUS_NO_MEMORY;
880
0
  }
881
882
0
  if (NULL == state->first_queued_frame) {
883
0
    state->first_queued_frame = link;
884
0
    state->last_queued_frame = link;
885
0
    link->next = NULL;
886
0
  } else {
887
0
    link->next = state->first_queued_frame;
888
0
    state->first_queued_frame = link;
889
0
  }
890
891
0
  return AMQP_STATUS_OK;
892
0
}
893
894
int amqp_simple_wait_frame_on_channel(amqp_connection_state_t state,
895
                                      amqp_channel_t channel,
896
0
                                      amqp_frame_t *decoded_frame) {
897
0
  amqp_frame_t *frame_ptr;
898
0
  amqp_link_t *cur;
899
0
  int res;
900
901
0
  for (cur = state->first_queued_frame; NULL != cur; cur = cur->next) {
902
0
    frame_ptr = cur->data;
903
904
0
    if (channel == frame_ptr->channel) {
905
0
      state->first_queued_frame = cur->next;
906
0
      if (NULL == state->first_queued_frame) {
907
0
        state->last_queued_frame = NULL;
908
0
      }
909
910
0
      *decoded_frame = *frame_ptr;
911
912
0
      return AMQP_STATUS_OK;
913
0
    }
914
0
  }
915
916
0
  for (;;) {
917
0
    res = wait_frame_inner(state, decoded_frame, amqp_time_infinite());
918
919
0
    if (AMQP_STATUS_OK != res) {
920
0
      return res;
921
0
    }
922
923
0
    if (channel == decoded_frame->channel) {
924
0
      return AMQP_STATUS_OK;
925
0
    } else {
926
0
      res = amqp_queue_frame(state, decoded_frame);
927
0
      if (res != AMQP_STATUS_OK) {
928
0
        return res;
929
0
      }
930
0
    }
931
0
  }
932
0
}
933
934
int amqp_simple_wait_frame(amqp_connection_state_t state,
935
0
                           amqp_frame_t *decoded_frame) {
936
0
  return amqp_simple_wait_frame_noblock(state, decoded_frame, NULL);
937
0
}
938
939
int amqp_simple_wait_frame_noblock(amqp_connection_state_t state,
940
                                   amqp_frame_t *decoded_frame,
941
0
                                   const struct timeval *timeout) {
942
0
  amqp_time_t deadline;
943
944
0
  int res = amqp_time_from_now(&deadline, timeout);
945
0
  if (AMQP_STATUS_OK != res) {
946
0
    return res;
947
0
  }
948
949
0
  if (state->first_queued_frame != NULL) {
950
0
    amqp_frame_t *f = (amqp_frame_t *)state->first_queued_frame->data;
951
0
    state->first_queued_frame = state->first_queued_frame->next;
952
0
    if (state->first_queued_frame == NULL) {
953
0
      state->last_queued_frame = NULL;
954
0
    }
955
0
    *decoded_frame = *f;
956
0
    return AMQP_STATUS_OK;
957
0
  } else {
958
0
    return wait_frame_inner(state, decoded_frame, deadline);
959
0
  }
960
0
}
961
962
static int amqp_simple_wait_method_list(amqp_connection_state_t state,
963
                                        amqp_channel_t expected_channel,
964
                                        amqp_method_number_t *expected_methods,
965
                                        amqp_time_t deadline,
966
0
                                        amqp_method_t *output) {
967
0
  amqp_frame_t frame;
968
0
  struct timeval tv;
969
0
  struct timeval *tvp;
970
971
0
  int res = amqp_time_tv_until(deadline, &tv, &tvp);
972
0
  if (res != AMQP_STATUS_OK) {
973
0
    return res;
974
0
  }
975
976
0
  res = amqp_simple_wait_frame_noblock(state, &frame, tvp);
977
0
  if (AMQP_STATUS_OK != res) {
978
0
    return res;
979
0
  }
980
981
0
  if (AMQP_FRAME_METHOD != frame.frame_type ||
982
0
      expected_channel != frame.channel ||
983
0
      !amqp_id_in_reply_list(frame.payload.method.id, expected_methods)) {
984
0
    return AMQP_STATUS_WRONG_METHOD;
985
0
  }
986
0
  *output = frame.payload.method;
987
0
  return AMQP_STATUS_OK;
988
0
}
989
990
static int simple_wait_method_inner(amqp_connection_state_t state,
991
                                    amqp_channel_t expected_channel,
992
                                    amqp_method_number_t expected_method,
993
                                    amqp_time_t deadline,
994
0
                                    amqp_method_t *output) {
995
0
  amqp_method_number_t expected_methods[2];
996
0
  expected_methods[0] = expected_method;
997
0
  expected_methods[1] = 0;
998
0
  return amqp_simple_wait_method_list(state, expected_channel, expected_methods,
999
0
                                      deadline, output);
1000
0
}
1001
1002
int amqp_simple_wait_method(amqp_connection_state_t state,
1003
                            amqp_channel_t expected_channel,
1004
                            amqp_method_number_t expected_method,
1005
0
                            amqp_method_t *output) {
1006
0
  return simple_wait_method_inner(state, expected_channel, expected_method,
1007
0
                                  amqp_time_infinite(), output);
1008
0
}
1009
1010
int amqp_send_method(amqp_connection_state_t state, amqp_channel_t channel,
1011
0
                     amqp_method_number_t id, void *decoded) {
1012
0
  return amqp_send_method_inner(state, channel, id, decoded, AMQP_SF_NONE,
1013
0
                                amqp_time_infinite());
1014
0
}
1015
1016
int amqp_send_method_inner(amqp_connection_state_t state,
1017
                           amqp_channel_t channel, amqp_method_number_t id,
1018
0
                           void *decoded, int flags, amqp_time_t deadline) {
1019
0
  amqp_frame_t frame;
1020
1021
0
  frame.frame_type = AMQP_FRAME_METHOD;
1022
0
  frame.channel = channel;
1023
0
  frame.payload.method.id = id;
1024
0
  frame.payload.method.decoded = decoded;
1025
0
  return amqp_send_frame_inner(state, &frame, flags, deadline);
1026
0
}
1027
1028
static int amqp_id_in_reply_list(amqp_method_number_t expected,
1029
0
                                 amqp_method_number_t *list) {
1030
0
  while (*list != 0) {
1031
0
    if (*list == expected) {
1032
0
      return 1;
1033
0
    }
1034
0
    list++;
1035
0
  }
1036
0
  return 0;
1037
0
}
1038
1039
static amqp_rpc_reply_t simple_rpc_inner(
1040
    amqp_connection_state_t state, amqp_channel_t channel,
1041
    amqp_method_number_t request_id, amqp_method_number_t *expected_reply_ids,
1042
0
    void *decoded_request_method, amqp_time_t deadline) {
1043
0
  int status;
1044
0
  amqp_rpc_reply_t result;
1045
1046
0
  memset(&result, 0, sizeof(result));
1047
1048
0
  status = amqp_send_method(state, channel, request_id, decoded_request_method);
1049
0
  if (status < 0) {
1050
0
    return amqp_rpc_reply_error(status);
1051
0
  }
1052
1053
0
  {
1054
0
    amqp_frame_t frame;
1055
1056
0
  retry:
1057
0
    status = wait_frame_inner(state, &frame, deadline);
1058
0
    if (status != AMQP_STATUS_OK) {
1059
0
      if (status == AMQP_STATUS_TIMEOUT) {
1060
0
        amqp_socket_close(state->socket, AMQP_SC_FORCE);
1061
0
      }
1062
0
      return amqp_rpc_reply_error(status);
1063
0
    }
1064
1065
    /*
1066
     * We store the frame for later processing unless it's something
1067
     * that directly affects us here, namely a method frame that is
1068
     * either
1069
     *  - on the channel we want, and of the expected type, or
1070
     *  - on the channel we want, and a channel.close frame, or
1071
     *  - on channel zero, and a connection.close frame.
1072
     */
1073
0
    if (!((frame.frame_type == AMQP_FRAME_METHOD) &&
1074
0
          (((frame.channel == channel) &&
1075
0
            (amqp_id_in_reply_list(frame.payload.method.id,
1076
0
                                   expected_reply_ids) ||
1077
0
             (frame.payload.method.id == AMQP_CHANNEL_CLOSE_METHOD))) ||
1078
0
           ((frame.channel == 0) &&
1079
0
            (frame.payload.method.id == AMQP_CONNECTION_CLOSE_METHOD))))) {
1080
0
      amqp_pool_t *channel_pool;
1081
0
      amqp_frame_t *frame_copy;
1082
0
      amqp_link_t *link;
1083
1084
0
      channel_pool = amqp_get_or_create_channel_pool(state, frame.channel);
1085
0
      if (NULL == channel_pool) {
1086
0
        return amqp_rpc_reply_error(AMQP_STATUS_NO_MEMORY);
1087
0
      }
1088
1089
0
      frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t));
1090
0
      link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t));
1091
1092
0
      if (frame_copy == NULL || link == NULL) {
1093
0
        return amqp_rpc_reply_error(AMQP_STATUS_NO_MEMORY);
1094
0
      }
1095
1096
0
      *frame_copy = frame;
1097
1098
0
      link->next = NULL;
1099
0
      link->data = frame_copy;
1100
1101
0
      if (state->last_queued_frame == NULL) {
1102
0
        state->first_queued_frame = link;
1103
0
      } else {
1104
0
        state->last_queued_frame->next = link;
1105
0
      }
1106
0
      state->last_queued_frame = link;
1107
1108
0
      goto retry;
1109
0
    }
1110
1111
0
    result.reply_type =
1112
0
        (amqp_id_in_reply_list(frame.payload.method.id, expected_reply_ids))
1113
0
            ? AMQP_RESPONSE_NORMAL
1114
0
            : AMQP_RESPONSE_SERVER_EXCEPTION;
1115
1116
0
    result.reply = frame.payload.method;
1117
0
    return result;
1118
0
  }
1119
0
}
1120
1121
amqp_rpc_reply_t amqp_simple_rpc(amqp_connection_state_t state,
1122
                                 amqp_channel_t channel,
1123
                                 amqp_method_number_t request_id,
1124
                                 amqp_method_number_t *expected_reply_ids,
1125
0
                                 void *decoded_request_method) {
1126
0
  amqp_time_t deadline;
1127
0
  int res;
1128
1129
0
  res = amqp_time_from_now(&deadline, state->rpc_timeout);
1130
0
  if (res != AMQP_STATUS_OK) {
1131
0
    return amqp_rpc_reply_error(res);
1132
0
  }
1133
1134
0
  return simple_rpc_inner(state, channel, request_id, expected_reply_ids,
1135
0
                          decoded_request_method, deadline);
1136
0
}
1137
1138
void *amqp_simple_rpc_decoded(amqp_connection_state_t state,
1139
                              amqp_channel_t channel,
1140
                              amqp_method_number_t request_id,
1141
                              amqp_method_number_t reply_id,
1142
0
                              void *decoded_request_method) {
1143
0
  amqp_time_t deadline;
1144
0
  int res;
1145
0
  amqp_method_number_t replies[2];
1146
1147
0
  res = amqp_time_from_now(&deadline, state->rpc_timeout);
1148
0
  if (res != AMQP_STATUS_OK) {
1149
0
    state->most_recent_api_result = amqp_rpc_reply_error(res);
1150
0
    return NULL;
1151
0
  }
1152
1153
0
  replies[0] = reply_id;
1154
0
  replies[1] = 0;
1155
1156
0
  state->most_recent_api_result = simple_rpc_inner(
1157
0
      state, channel, request_id, replies, decoded_request_method, deadline);
1158
1159
0
  if (state->most_recent_api_result.reply_type == AMQP_RESPONSE_NORMAL) {
1160
0
    return state->most_recent_api_result.reply.decoded;
1161
0
  } else {
1162
0
    return NULL;
1163
0
  }
1164
0
}
1165
1166
0
amqp_rpc_reply_t amqp_get_rpc_reply(amqp_connection_state_t state) {
1167
0
  return state->most_recent_api_result;
1168
0
}
1169
1170
/*
1171
 * Merge base and add tables. If the two tables contain an entry with the same
1172
 * key, the entry from the add table takes precedence. For entries that are both
1173
 * tables with the same key, the table is recursively merged.
1174
 */
1175
int amqp_merge_capabilities(const amqp_table_t *base, const amqp_table_t *add,
1176
0
                            amqp_table_t *result, amqp_pool_t *pool) {
1177
0
  int i;
1178
0
  int res;
1179
0
  amqp_pool_t temp_pool;
1180
0
  amqp_table_t temp_result;
1181
0
  assert(base != NULL);
1182
0
  assert(result != NULL);
1183
0
  assert(pool != NULL);
1184
1185
0
  if (NULL == add) {
1186
0
    return amqp_table_clone(base, result, pool);
1187
0
  }
1188
1189
0
  init_amqp_pool(&temp_pool, 4096);
1190
0
  temp_result.num_entries = 0;
1191
0
  temp_result.entries =
1192
0
      amqp_pool_alloc(&temp_pool, sizeof(amqp_table_entry_t) *
1193
0
                                      (base->num_entries + add->num_entries));
1194
0
  if (NULL == temp_result.entries) {
1195
0
    res = AMQP_STATUS_NO_MEMORY;
1196
0
    goto error_out;
1197
0
  }
1198
0
  for (i = 0; i < base->num_entries; ++i) {
1199
0
    temp_result.entries[temp_result.num_entries] = base->entries[i];
1200
0
    temp_result.num_entries++;
1201
0
  }
1202
0
  for (i = 0; i < add->num_entries; ++i) {
1203
0
    amqp_table_entry_t *e =
1204
0
        amqp_table_get_entry_by_key(&temp_result, add->entries[i].key);
1205
0
    if (NULL != e) {
1206
0
      if (AMQP_FIELD_KIND_TABLE == add->entries[i].value.kind &&
1207
0
          AMQP_FIELD_KIND_TABLE == e->value.kind) {
1208
0
        amqp_table_entry_t *be =
1209
0
            amqp_table_get_entry_by_key(base, add->entries[i].key);
1210
1211
0
        res = amqp_merge_capabilities(&be->value.value.table,
1212
0
                                      &add->entries[i].value.value.table,
1213
0
                                      &e->value.value.table, &temp_pool);
1214
0
        if (AMQP_STATUS_OK != res) {
1215
0
          goto error_out;
1216
0
        }
1217
0
      } else {
1218
0
        e->value = add->entries[i].value;
1219
0
      }
1220
0
    } else {
1221
0
      temp_result.entries[temp_result.num_entries] = add->entries[i];
1222
0
      temp_result.num_entries++;
1223
0
    }
1224
0
  }
1225
0
  res = amqp_table_clone(&temp_result, result, pool);
1226
0
error_out:
1227
0
  empty_amqp_pool(&temp_pool);
1228
0
  return res;
1229
0
}
1230
1231
static amqp_rpc_reply_t amqp_login_inner(amqp_connection_state_t state,
1232
                                         char const *vhost, int channel_max,
1233
                                         int frame_max, int heartbeat,
1234
                                         const amqp_table_t *client_properties,
1235
                                         const struct timeval *timeout,
1236
                                         amqp_sasl_method_enum sasl_method,
1237
0
                                         va_list vl) {
1238
0
  int res;
1239
0
  amqp_method_t method;
1240
1241
0
  uint16_t client_channel_max;
1242
0
  uint32_t client_frame_max;
1243
0
  uint16_t client_heartbeat;
1244
1245
0
  uint16_t server_channel_max;
1246
0
  uint32_t server_frame_max;
1247
0
  uint16_t server_heartbeat;
1248
1249
0
  amqp_rpc_reply_t result;
1250
0
  amqp_time_t deadline;
1251
1252
0
  if (channel_max < 0 || channel_max > UINT16_MAX) {
1253
0
    return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
1254
0
  }
1255
0
  client_channel_max = (uint16_t)channel_max;
1256
1257
0
  if (frame_max < 0) {
1258
0
    return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
1259
0
  }
1260
0
  client_frame_max = (uint32_t)frame_max;
1261
1262
0
  if (heartbeat < 0 || heartbeat > UINT16_MAX) {
1263
0
    return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
1264
0
  }
1265
0
  client_heartbeat = (uint16_t)heartbeat;
1266
1267
0
  res = amqp_time_from_now(&deadline, timeout);
1268
0
  if (AMQP_STATUS_OK != res) {
1269
0
    goto error_res;
1270
0
  }
1271
1272
0
  res = send_header_inner(state, deadline);
1273
0
  if (AMQP_STATUS_OK != res) {
1274
0
    goto error_res;
1275
0
  }
1276
1277
0
  res = simple_wait_method_inner(state, 0, AMQP_CONNECTION_START_METHOD,
1278
0
                                 deadline, &method);
1279
0
  if (AMQP_STATUS_OK != res) {
1280
0
    goto error_res;
1281
0
  }
1282
1283
0
  {
1284
0
    amqp_connection_start_t *s = (amqp_connection_start_t *)method.decoded;
1285
0
    if ((s->version_major != AMQP_PROTOCOL_VERSION_MAJOR) ||
1286
0
        (s->version_minor != AMQP_PROTOCOL_VERSION_MINOR)) {
1287
0
      res = AMQP_STATUS_INCOMPATIBLE_AMQP_VERSION;
1288
0
      goto error_res;
1289
0
    }
1290
1291
0
    res = amqp_table_clone(&s->server_properties, &state->server_properties,
1292
0
                           &state->properties_pool);
1293
1294
0
    if (AMQP_STATUS_OK != res) {
1295
0
      goto error_res;
1296
0
    }
1297
1298
    /* TODO: check that our chosen SASL mechanism is in the list of
1299
       acceptable mechanisms. Or even let the application choose from
1300
       the list! */
1301
0
    if (!sasl_mechanism_in_list(s->mechanisms, sasl_method)) {
1302
0
      res = AMQP_STATUS_BROKER_UNSUPPORTED_SASL_METHOD;
1303
0
      goto error_res;
1304
0
    }
1305
0
  }
1306
1307
0
  {
1308
0
    amqp_table_entry_t default_properties[6];
1309
0
    amqp_table_t default_table;
1310
0
    amqp_table_entry_t client_capabilities[2];
1311
0
    amqp_table_t client_capabilities_table;
1312
0
    amqp_connection_start_ok_t s;
1313
0
    amqp_pool_t *channel_pool;
1314
0
    amqp_bytes_t response_bytes;
1315
1316
0
    channel_pool = amqp_get_or_create_channel_pool(state, 0);
1317
0
    if (NULL == channel_pool) {
1318
0
      res = AMQP_STATUS_NO_MEMORY;
1319
0
      goto error_res;
1320
0
    }
1321
1322
0
    response_bytes = sasl_response(channel_pool, sasl_method, vl, &res);
1323
0
    if (AMQP_STATUS_OK != res) {
1324
      /* Note: response_bytes.bytes == NULL is not itself an error here: a
1325
         zero-length response (e.g. AMQP_SASL_METHOD_EXTERNAL with an empty
1326
         identity) is a valid, successful result. sasl_response() reports
1327
         real allocation failures via res. */
1328
0
      goto error_res;
1329
0
    }
1330
1331
0
    client_capabilities[0] =
1332
0
        amqp_table_construct_bool_entry("authentication_failure_close", 1);
1333
0
    client_capabilities[1] =
1334
0
        amqp_table_construct_bool_entry("exchange_exchange_bindings", 1);
1335
1336
0
    client_capabilities_table.entries = client_capabilities;
1337
0
    client_capabilities_table.num_entries =
1338
0
        sizeof(client_capabilities) / sizeof(amqp_table_entry_t);
1339
1340
0
    default_properties[0] =
1341
0
        amqp_table_construct_utf8_entry("product", "rabbitmq-c");
1342
0
    default_properties[1] =
1343
0
        amqp_table_construct_utf8_entry("version", AMQP_VERSION_STRING);
1344
0
    default_properties[2] =
1345
0
        amqp_table_construct_utf8_entry("platform", AMQ_PLATFORM);
1346
0
    default_properties[3] =
1347
0
        amqp_table_construct_utf8_entry("copyright", AMQ_COPYRIGHT);
1348
0
    default_properties[4] = amqp_table_construct_utf8_entry(
1349
0
        "information", "See https://github.com/alanxz/rabbitmq-c");
1350
0
    default_properties[5] = amqp_table_construct_table_entry(
1351
0
        "capabilities", &client_capabilities_table);
1352
1353
0
    default_table.entries = default_properties;
1354
0
    default_table.num_entries =
1355
0
        sizeof(default_properties) / sizeof(amqp_table_entry_t);
1356
1357
0
    res = amqp_merge_capabilities(&default_table, client_properties,
1358
0
                                  &state->client_properties, channel_pool);
1359
0
    if (AMQP_STATUS_OK != res) {
1360
0
      goto error_res;
1361
0
    }
1362
1363
0
    s.client_properties = state->client_properties;
1364
0
    s.mechanism = sasl_method_name(sasl_method);
1365
0
    s.response = response_bytes;
1366
0
    s.locale = amqp_literal_bytes("en_US");
1367
1368
0
    res = amqp_send_method_inner(state, 0, AMQP_CONNECTION_START_OK_METHOD, &s,
1369
0
                                 AMQP_SF_NONE, deadline);
1370
0
    if (res < 0) {
1371
0
      goto error_res;
1372
0
    }
1373
0
  }
1374
1375
0
  amqp_release_buffers(state);
1376
1377
0
  {
1378
0
    amqp_method_number_t expected[] = {AMQP_CONNECTION_TUNE_METHOD,
1379
0
                                       AMQP_CONNECTION_CLOSE_METHOD, 0};
1380
1381
0
    res = amqp_simple_wait_method_list(state, 0, expected, deadline, &method);
1382
0
    if (AMQP_STATUS_OK != res) {
1383
0
      goto error_res;
1384
0
    }
1385
0
  }
1386
1387
0
  if (AMQP_CONNECTION_CLOSE_METHOD == method.id) {
1388
0
    result.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION;
1389
0
    result.reply = method;
1390
0
    result.library_error = 0;
1391
0
    goto out;
1392
0
  }
1393
1394
0
  {
1395
0
    amqp_connection_tune_t *s = (amqp_connection_tune_t *)method.decoded;
1396
0
    server_channel_max = s->channel_max;
1397
0
    server_frame_max = s->frame_max;
1398
0
    server_heartbeat = s->heartbeat;
1399
0
  }
1400
1401
0
  if (server_channel_max != 0 &&
1402
0
      (server_channel_max < client_channel_max || client_channel_max == 0)) {
1403
0
    client_channel_max = server_channel_max;
1404
0
  } else if (server_channel_max == 0 && client_channel_max == 0) {
1405
0
    client_channel_max = UINT16_MAX;
1406
0
  }
1407
1408
0
  if (server_frame_max != 0 && server_frame_max < client_frame_max) {
1409
0
    client_frame_max = server_frame_max;
1410
0
  }
1411
1412
0
  if (server_heartbeat != 0 && server_heartbeat < client_heartbeat) {
1413
0
    client_heartbeat = server_heartbeat;
1414
0
  }
1415
1416
0
  res = amqp_tune_connection(state, client_channel_max, client_frame_max,
1417
0
                             client_heartbeat);
1418
0
  if (res < 0) {
1419
0
    goto error_res;
1420
0
  }
1421
0
  client_frame_max = (uint32_t)amqp_get_frame_max(state);
1422
1423
0
  {
1424
0
    amqp_connection_tune_ok_t s;
1425
0
    s.frame_max = client_frame_max;
1426
0
    s.channel_max = client_channel_max;
1427
0
    s.heartbeat = client_heartbeat;
1428
1429
0
    res = amqp_send_method_inner(state, 0, AMQP_CONNECTION_TUNE_OK_METHOD, &s,
1430
0
                                 AMQP_SF_NONE, deadline);
1431
0
    if (res < 0) {
1432
0
      goto error_res;
1433
0
    }
1434
0
  }
1435
1436
0
  amqp_release_buffers(state);
1437
1438
0
  {
1439
0
    amqp_method_number_t replies[] = {AMQP_CONNECTION_OPEN_OK_METHOD, 0};
1440
0
    amqp_connection_open_t s;
1441
0
    s.virtual_host = amqp_cstring_bytes(vhost);
1442
0
    s.capabilities = amqp_empty_bytes;
1443
0
    s.insist = 1;
1444
1445
0
    result = simple_rpc_inner(state, 0, AMQP_CONNECTION_OPEN_METHOD, replies,
1446
0
                              &s, deadline);
1447
0
    if (result.reply_type != AMQP_RESPONSE_NORMAL) {
1448
0
      goto out;
1449
0
    }
1450
0
  }
1451
1452
0
  result.reply_type = AMQP_RESPONSE_NORMAL;
1453
0
  result.reply.id = 0;
1454
0
  result.reply.decoded = NULL;
1455
0
  result.library_error = 0;
1456
0
  amqp_maybe_release_buffers(state);
1457
1458
0
out:
1459
0
  return result;
1460
1461
0
error_res:
1462
0
  amqp_socket_close(state->socket, AMQP_SC_FORCE);
1463
0
  result = amqp_rpc_reply_error(res);
1464
1465
0
  goto out;
1466
0
}
1467
1468
amqp_rpc_reply_t amqp_login(amqp_connection_state_t state, char const *vhost,
1469
                            int channel_max, int frame_max, int heartbeat,
1470
0
                            amqp_sasl_method_enum sasl_method, ...) {
1471
1472
0
  va_list vl;
1473
0
  amqp_rpc_reply_t ret;
1474
1475
0
  va_start(vl, sasl_method);
1476
1477
0
  ret = amqp_login_inner(state, vhost, channel_max, frame_max, heartbeat,
1478
0
                         &amqp_empty_table, state->handshake_timeout,
1479
0
                         sasl_method, vl);
1480
1481
0
  va_end(vl);
1482
1483
0
  return ret;
1484
0
}
1485
1486
amqp_rpc_reply_t amqp_login_with_properties(
1487
    amqp_connection_state_t state, char const *vhost, int channel_max,
1488
    int frame_max, int heartbeat, const amqp_table_t *client_properties,
1489
0
    amqp_sasl_method_enum sasl_method, ...) {
1490
0
  va_list vl;
1491
0
  amqp_rpc_reply_t ret;
1492
1493
0
  va_start(vl, sasl_method);
1494
1495
0
  ret = amqp_login_inner(state, vhost, channel_max, frame_max, heartbeat,
1496
0
                         client_properties, state->handshake_timeout,
1497
0
                         sasl_method, vl);
1498
1499
0
  va_end(vl);
1500
1501
0
  return ret;
1502
0
}