Coverage Report

Created: 2026-07-16 06:59

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
44
                         int flags) {
91
44
  assert(self);
92
44
  assert(self->klass->send);
93
44
  return self->klass->send(self, buf, len, flags);
94
44
}
95
96
ssize_t amqp_socket_recv(amqp_socket_t *self, void *buf, size_t len,
97
49
                         int flags) {
98
49
  assert(self);
99
49
  assert(self->klass->recv);
100
49
  return self->klass->recv(self, buf, len, flags);
101
49
}
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
44
int amqp_socket_close(amqp_socket_t *self, amqp_socket_close_enum force) {
117
44
  assert(self);
118
44
  assert(self->klass->close);
119
44
  return self->klass->close(self, force);
120
44
}
121
122
88
void amqp_socket_delete(amqp_socket_t *self) {
123
88
  if (self) {
124
44
    assert(self->klass->delete);
125
44
    self->klass->delete (self);
126
44
  }
127
88
}
128
129
5
int amqp_socket_get_sockfd(amqp_socket_t *self) {
130
5
  assert(self);
131
5
  assert(self->klass->get_sockfd);
132
5
  return self->klass->get_sockfd(self);
133
5
}
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
44
                      size_t len, amqp_time_t deadline, int flags) {
251
44
  ssize_t res;
252
44
  void *buf_left = (void *)buf;
253
  /* Assume that len is not going to be larger than ssize_t can hold. */
254
44
  ssize_t len_left = (size_t)len;
255
256
44
start_send:
257
44
  res = amqp_socket_send(state->socket, buf_left, len_left, flags);
258
259
44
  if (res > 0) {
260
44
    len_left -= res;
261
44
    buf_left = (char *)buf_left + res;
262
44
    if (0 == len_left) {
263
44
      return (ssize_t)len;
264
44
    }
265
0
    goto start_send;
266
44
  }
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
44
                             amqp_time_t deadline) {
487
44
  ssize_t res;
488
44
  static const uint8_t header[8] = {'A',
489
44
                                    'M',
490
44
                                    'Q',
491
44
                                    'P',
492
44
                                    0,
493
44
                                    AMQP_PROTOCOL_VERSION_MAJOR,
494
44
                                    AMQP_PROTOCOL_VERSION_MINOR,
495
44
                                    AMQP_PROTOCOL_VERSION_REVISION};
496
44
  res = amqp_try_send(state, header, sizeof(header), deadline, AMQP_SF_NONE);
497
44
  if (sizeof(header) == res) {
498
44
    return AMQP_STATUS_OK;
499
44
  }
500
0
  return (int)res;
501
44
}
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
0
                                  amqp_sasl_method_enum method, va_list args) {
573
0
  amqp_bytes_t response;
574
575
0
  switch (method) {
576
0
    case AMQP_SASL_METHOD_PLAIN: {
577
0
      char *username = va_arg(args, char *);
578
0
      size_t username_len = strlen(username);
579
0
      char *password = va_arg(args, char *);
580
0
      size_t password_len = strlen(password);
581
0
      char *response_buf;
582
583
0
      amqp_pool_alloc_bytes(pool, strlen(username) + strlen(password) + 2,
584
0
                            &response);
585
0
      if (response.bytes == NULL)
586
      /* We never request a zero-length block, because of the +2
587
         above, so a NULL here really is ENOMEM. */
588
0
      {
589
0
        return response;
590
0
      }
591
592
0
      response_buf = response.bytes;
593
0
      response_buf[0] = 0;
594
0
      memcpy(response_buf + 1, username, username_len);
595
0
      response_buf[username_len + 1] = 0;
596
0
      memcpy(response_buf + username_len + 2, password, password_len);
597
0
      break;
598
0
    }
599
0
    case AMQP_SASL_METHOD_EXTERNAL: {
600
0
      char *identity = va_arg(args, char *);
601
0
      size_t identity_len = strlen(identity);
602
603
0
      amqp_pool_alloc_bytes(pool, identity_len, &response);
604
0
      if (response.bytes == NULL) {
605
0
        return response;
606
0
      }
607
608
0
      memcpy(response.bytes, identity, identity_len);
609
0
      break;
610
0
    }
611
0
    default:
612
0
      amqp_abort("Invalid SASL method: %d", (int)method);
613
0
  }
614
615
0
  return response;
616
0
}
617
618
0
amqp_boolean_t amqp_frames_enqueued(amqp_connection_state_t state) {
619
0
  return (state->first_queued_frame != NULL);
620
0
}
621
622
/*
623
 * Check to see if we have data in our buffer. If this returns 1, we
624
 * will avoid an immediate blocking read in amqp_simple_wait_frame.
625
 */
626
111
amqp_boolean_t amqp_data_in_buffer(amqp_connection_state_t state) {
627
111
  return (state->sock_inbound_offset < state->sock_inbound_limit);
628
111
}
629
630
static int consume_one_frame(amqp_connection_state_t state,
631
62
                             amqp_frame_t *decoded_frame) {
632
62
  int res;
633
634
62
  amqp_bytes_t buffer;
635
62
  buffer.len = state->sock_inbound_limit - state->sock_inbound_offset;
636
62
  buffer.bytes =
637
62
      ((char *)state->sock_inbound_buffer.bytes) + state->sock_inbound_offset;
638
639
62
  res = amqp_handle_input(state, buffer, decoded_frame);
640
62
  if (res < 0) {
641
39
    return res;
642
39
  }
643
644
23
  state->sock_inbound_offset += res;
645
646
23
  return AMQP_STATUS_OK;
647
62
}
648
649
static int recv_with_timeout(amqp_connection_state_t state,
650
49
                             amqp_time_t timeout) {
651
49
  ssize_t res;
652
49
  int fd;
653
654
49
start_recv:
655
49
  res = amqp_socket_recv(state->socket, state->sock_inbound_buffer.bytes,
656
49
                         state->sock_inbound_buffer.len, 0);
657
658
49
  if (res < 0) {
659
5
    fd = amqp_get_sockfd(state);
660
5
    if (-1 == fd) {
661
0
      return AMQP_STATUS_CONNECTION_CLOSED;
662
0
    }
663
5
    switch (res) {
664
5
      default:
665
5
        return (int)res;
666
0
      case AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD:
667
0
        res = amqp_poll(fd, AMQP_SF_POLLIN, timeout);
668
0
        break;
669
0
      case AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE:
670
0
        res = amqp_poll(fd, AMQP_SF_POLLOUT, timeout);
671
0
        break;
672
5
    }
673
0
    if (AMQP_STATUS_OK == res) {
674
0
      goto start_recv;
675
0
    }
676
0
    return (int)res;
677
0
  }
678
679
44
  state->sock_inbound_limit = res;
680
44
  state->sock_inbound_offset = 0;
681
682
44
  res = amqp_time_s_from_now(&state->next_recv_heartbeat,
683
44
                             amqp_heartbeat_recv(state));
684
44
  if (AMQP_STATUS_OK != res) {
685
0
    return (int)res;
686
0
  }
687
44
  return AMQP_STATUS_OK;
688
44
}
689
690
0
int amqp_try_recv(amqp_connection_state_t state) {
691
0
  amqp_time_t timeout;
692
0
  int res;
693
694
0
  while (amqp_data_in_buffer(state)) {
695
0
    amqp_frame_t frame;
696
0
    res = consume_one_frame(state, &frame);
697
698
0
    if (AMQP_STATUS_OK != res) {
699
0
      return res;
700
0
    }
701
702
0
    if (frame.frame_type != 0) {
703
0
      amqp_pool_t *channel_pool;
704
0
      amqp_frame_t *frame_copy;
705
0
      amqp_link_t *link;
706
707
0
      channel_pool = amqp_get_or_create_channel_pool(state, frame.channel);
708
0
      if (NULL == channel_pool) {
709
0
        return AMQP_STATUS_NO_MEMORY;
710
0
      }
711
712
0
      frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t));
713
0
      link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t));
714
715
0
      if (frame_copy == NULL || link == NULL) {
716
0
        return AMQP_STATUS_NO_MEMORY;
717
0
      }
718
719
0
      *frame_copy = frame;
720
721
0
      link->next = NULL;
722
0
      link->data = frame_copy;
723
724
0
      if (state->last_queued_frame == NULL) {
725
0
        state->first_queued_frame = link;
726
0
      } else {
727
0
        state->last_queued_frame->next = link;
728
0
      }
729
0
      state->last_queued_frame = link;
730
0
    }
731
0
  }
732
0
  res = amqp_time_from_now(&timeout, &(struct timeval){0});
733
0
  if (AMQP_STATUS_OK != res) {
734
0
    return res;
735
0
  }
736
737
0
  return recv_with_timeout(state, timeout);
738
0
}
739
740
static int wait_frame_inner(amqp_connection_state_t state,
741
                            amqp_frame_t *decoded_frame,
742
44
                            amqp_time_t timeout_deadline) {
743
44
  amqp_time_t deadline;
744
44
  int res;
745
746
88
  for (;;) {
747
111
    while (amqp_data_in_buffer(state)) {
748
62
      res = consume_one_frame(state, decoded_frame);
749
750
62
      if (AMQP_STATUS_OK != res) {
751
39
        return res;
752
39
      }
753
754
23
      if (AMQP_FRAME_HEARTBEAT == decoded_frame->frame_type) {
755
6
        amqp_maybe_release_buffers_on_channel(state, 0);
756
6
        continue;
757
6
      }
758
759
17
      if (decoded_frame->frame_type != 0) {
760
        /* Complete frame was read. Return it. */
761
0
        return AMQP_STATUS_OK;
762
0
      }
763
17
    }
764
765
49
  beginrecv:
766
49
    res = amqp_time_has_past(state->next_send_heartbeat);
767
49
    if (AMQP_STATUS_TIMER_FAILURE == res) {
768
0
      return res;
769
49
    } else if (AMQP_STATUS_TIMEOUT == res) {
770
0
      amqp_frame_t heartbeat;
771
0
      heartbeat.channel = 0;
772
0
      heartbeat.frame_type = AMQP_FRAME_HEARTBEAT;
773
774
0
      res = amqp_send_frame(state, &heartbeat);
775
0
      if (AMQP_STATUS_OK != res) {
776
0
        return res;
777
0
      }
778
0
    }
779
49
    deadline = amqp_time_first(timeout_deadline,
780
49
                               amqp_time_first(state->next_recv_heartbeat,
781
49
                                               state->next_send_heartbeat));
782
783
    /* TODO this needs to wait for a _frame_ and not anything written from the
784
     * socket */
785
49
    res = recv_with_timeout(state, deadline);
786
787
49
    if (AMQP_STATUS_TIMEOUT == res) {
788
0
      if (amqp_time_equal(deadline, state->next_recv_heartbeat)) {
789
0
        amqp_socket_close(state->socket, AMQP_SC_FORCE);
790
0
        return AMQP_STATUS_HEARTBEAT_TIMEOUT;
791
0
      } else if (amqp_time_equal(deadline, timeout_deadline)) {
792
0
        return AMQP_STATUS_TIMEOUT;
793
0
      } else if (amqp_time_equal(deadline, state->next_send_heartbeat)) {
794
        /* send heartbeat happens before we do recv_with_timeout */
795
0
        goto beginrecv;
796
0
      } else {
797
0
        amqp_abort("Internal error: unable to determine timeout reason");
798
0
      }
799
49
    } else if (AMQP_STATUS_OK != res) {
800
5
      return res;
801
5
    }
802
49
  }
803
44
}
804
805
static amqp_link_t *amqp_create_link_for_frame(amqp_connection_state_t state,
806
0
                                               amqp_frame_t *frame) {
807
0
  amqp_link_t *link;
808
0
  amqp_frame_t *frame_copy;
809
810
0
  amqp_pool_t *channel_pool =
811
0
      amqp_get_or_create_channel_pool(state, frame->channel);
812
813
0
  if (NULL == channel_pool) {
814
0
    return NULL;
815
0
  }
816
817
0
  link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t));
818
0
  frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t));
819
820
0
  if (NULL == link || NULL == frame_copy) {
821
0
    return NULL;
822
0
  }
823
824
0
  *frame_copy = *frame;
825
0
  link->data = frame_copy;
826
827
0
  return link;
828
0
}
829
830
0
int amqp_queue_frame(amqp_connection_state_t state, amqp_frame_t *frame) {
831
0
  amqp_link_t *link = amqp_create_link_for_frame(state, frame);
832
0
  if (NULL == link) {
833
0
    return AMQP_STATUS_NO_MEMORY;
834
0
  }
835
836
0
  if (NULL == state->first_queued_frame) {
837
0
    state->first_queued_frame = link;
838
0
  } else {
839
0
    state->last_queued_frame->next = link;
840
0
  }
841
842
0
  link->next = NULL;
843
0
  state->last_queued_frame = link;
844
845
0
  return AMQP_STATUS_OK;
846
0
}
847
848
0
int amqp_put_back_frame(amqp_connection_state_t state, amqp_frame_t *frame) {
849
0
  amqp_link_t *link = amqp_create_link_for_frame(state, frame);
850
0
  if (NULL == link) {
851
0
    return AMQP_STATUS_NO_MEMORY;
852
0
  }
853
854
0
  if (NULL == state->first_queued_frame) {
855
0
    state->first_queued_frame = link;
856
0
    state->last_queued_frame = link;
857
0
    link->next = NULL;
858
0
  } else {
859
0
    link->next = state->first_queued_frame;
860
0
    state->first_queued_frame = link;
861
0
  }
862
863
0
  return AMQP_STATUS_OK;
864
0
}
865
866
int amqp_simple_wait_frame_on_channel(amqp_connection_state_t state,
867
                                      amqp_channel_t channel,
868
0
                                      amqp_frame_t *decoded_frame) {
869
0
  amqp_frame_t *frame_ptr;
870
0
  amqp_link_t *cur;
871
0
  int res;
872
873
0
  for (cur = state->first_queued_frame; NULL != cur; cur = cur->next) {
874
0
    frame_ptr = cur->data;
875
876
0
    if (channel == frame_ptr->channel) {
877
0
      state->first_queued_frame = cur->next;
878
0
      if (NULL == state->first_queued_frame) {
879
0
        state->last_queued_frame = NULL;
880
0
      }
881
882
0
      *decoded_frame = *frame_ptr;
883
884
0
      return AMQP_STATUS_OK;
885
0
    }
886
0
  }
887
888
0
  for (;;) {
889
0
    res = wait_frame_inner(state, decoded_frame, amqp_time_infinite());
890
891
0
    if (AMQP_STATUS_OK != res) {
892
0
      return res;
893
0
    }
894
895
0
    if (channel == decoded_frame->channel) {
896
0
      return AMQP_STATUS_OK;
897
0
    } else {
898
0
      res = amqp_queue_frame(state, decoded_frame);
899
0
      if (res != AMQP_STATUS_OK) {
900
0
        return res;
901
0
      }
902
0
    }
903
0
  }
904
0
}
905
906
int amqp_simple_wait_frame(amqp_connection_state_t state,
907
0
                           amqp_frame_t *decoded_frame) {
908
0
  return amqp_simple_wait_frame_noblock(state, decoded_frame, NULL);
909
0
}
910
911
int amqp_simple_wait_frame_noblock(amqp_connection_state_t state,
912
                                   amqp_frame_t *decoded_frame,
913
44
                                   const struct timeval *timeout) {
914
44
  amqp_time_t deadline;
915
916
44
  int res = amqp_time_from_now(&deadline, timeout);
917
44
  if (AMQP_STATUS_OK != res) {
918
0
    return res;
919
0
  }
920
921
44
  if (state->first_queued_frame != NULL) {
922
0
    amqp_frame_t *f = (amqp_frame_t *)state->first_queued_frame->data;
923
0
    state->first_queued_frame = state->first_queued_frame->next;
924
0
    if (state->first_queued_frame == NULL) {
925
0
      state->last_queued_frame = NULL;
926
0
    }
927
0
    *decoded_frame = *f;
928
0
    return AMQP_STATUS_OK;
929
44
  } else {
930
44
    return wait_frame_inner(state, decoded_frame, deadline);
931
44
  }
932
44
}
933
934
static int amqp_simple_wait_method_list(amqp_connection_state_t state,
935
                                        amqp_channel_t expected_channel,
936
                                        amqp_method_number_t *expected_methods,
937
                                        amqp_time_t deadline,
938
44
                                        amqp_method_t *output) {
939
44
  amqp_frame_t frame;
940
44
  struct timeval tv;
941
44
  struct timeval *tvp;
942
943
44
  int res = amqp_time_tv_until(deadline, &tv, &tvp);
944
44
  if (res != AMQP_STATUS_OK) {
945
0
    return res;
946
0
  }
947
948
44
  res = amqp_simple_wait_frame_noblock(state, &frame, tvp);
949
44
  if (AMQP_STATUS_OK != res) {
950
44
    return res;
951
44
  }
952
953
0
  if (AMQP_FRAME_METHOD != frame.frame_type ||
954
0
      expected_channel != frame.channel ||
955
0
      !amqp_id_in_reply_list(frame.payload.method.id, expected_methods)) {
956
0
    return AMQP_STATUS_WRONG_METHOD;
957
0
  }
958
0
  *output = frame.payload.method;
959
0
  return AMQP_STATUS_OK;
960
0
}
961
962
static int simple_wait_method_inner(amqp_connection_state_t state,
963
                                    amqp_channel_t expected_channel,
964
                                    amqp_method_number_t expected_method,
965
                                    amqp_time_t deadline,
966
44
                                    amqp_method_t *output) {
967
44
  amqp_method_number_t expected_methods[2];
968
44
  expected_methods[0] = expected_method;
969
44
  expected_methods[1] = 0;
970
44
  return amqp_simple_wait_method_list(state, expected_channel, expected_methods,
971
44
                                      deadline, output);
972
44
}
973
974
int amqp_simple_wait_method(amqp_connection_state_t state,
975
                            amqp_channel_t expected_channel,
976
                            amqp_method_number_t expected_method,
977
0
                            amqp_method_t *output) {
978
0
  return simple_wait_method_inner(state, expected_channel, expected_method,
979
0
                                  amqp_time_infinite(), output);
980
0
}
981
982
int amqp_send_method(amqp_connection_state_t state, amqp_channel_t channel,
983
0
                     amqp_method_number_t id, void *decoded) {
984
0
  return amqp_send_method_inner(state, channel, id, decoded, AMQP_SF_NONE,
985
0
                                amqp_time_infinite());
986
0
}
987
988
int amqp_send_method_inner(amqp_connection_state_t state,
989
                           amqp_channel_t channel, amqp_method_number_t id,
990
0
                           void *decoded, int flags, amqp_time_t deadline) {
991
0
  amqp_frame_t frame;
992
993
0
  frame.frame_type = AMQP_FRAME_METHOD;
994
0
  frame.channel = channel;
995
0
  frame.payload.method.id = id;
996
0
  frame.payload.method.decoded = decoded;
997
0
  return amqp_send_frame_inner(state, &frame, flags, deadline);
998
0
}
999
1000
static int amqp_id_in_reply_list(amqp_method_number_t expected,
1001
0
                                 amqp_method_number_t *list) {
1002
0
  while (*list != 0) {
1003
0
    if (*list == expected) {
1004
0
      return 1;
1005
0
    }
1006
0
    list++;
1007
0
  }
1008
0
  return 0;
1009
0
}
1010
1011
static amqp_rpc_reply_t simple_rpc_inner(
1012
    amqp_connection_state_t state, amqp_channel_t channel,
1013
    amqp_method_number_t request_id, amqp_method_number_t *expected_reply_ids,
1014
0
    void *decoded_request_method, amqp_time_t deadline) {
1015
0
  int status;
1016
0
  amqp_rpc_reply_t result;
1017
1018
0
  memset(&result, 0, sizeof(result));
1019
1020
0
  status = amqp_send_method(state, channel, request_id, decoded_request_method);
1021
0
  if (status < 0) {
1022
0
    return amqp_rpc_reply_error(status);
1023
0
  }
1024
1025
0
  {
1026
0
    amqp_frame_t frame;
1027
1028
0
  retry:
1029
0
    status = wait_frame_inner(state, &frame, deadline);
1030
0
    if (status != AMQP_STATUS_OK) {
1031
0
      if (status == AMQP_STATUS_TIMEOUT) {
1032
0
        amqp_socket_close(state->socket, AMQP_SC_FORCE);
1033
0
      }
1034
0
      return amqp_rpc_reply_error(status);
1035
0
    }
1036
1037
    /*
1038
     * We store the frame for later processing unless it's something
1039
     * that directly affects us here, namely a method frame that is
1040
     * either
1041
     *  - on the channel we want, and of the expected type, or
1042
     *  - on the channel we want, and a channel.close frame, or
1043
     *  - on channel zero, and a connection.close frame.
1044
     */
1045
0
    if (!((frame.frame_type == AMQP_FRAME_METHOD) &&
1046
0
          (((frame.channel == channel) &&
1047
0
            (amqp_id_in_reply_list(frame.payload.method.id,
1048
0
                                   expected_reply_ids) ||
1049
0
             (frame.payload.method.id == AMQP_CHANNEL_CLOSE_METHOD))) ||
1050
0
           ((frame.channel == 0) &&
1051
0
            (frame.payload.method.id == AMQP_CONNECTION_CLOSE_METHOD))))) {
1052
0
      amqp_pool_t *channel_pool;
1053
0
      amqp_frame_t *frame_copy;
1054
0
      amqp_link_t *link;
1055
1056
0
      channel_pool = amqp_get_or_create_channel_pool(state, frame.channel);
1057
0
      if (NULL == channel_pool) {
1058
0
        return amqp_rpc_reply_error(AMQP_STATUS_NO_MEMORY);
1059
0
      }
1060
1061
0
      frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t));
1062
0
      link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t));
1063
1064
0
      if (frame_copy == NULL || link == NULL) {
1065
0
        return amqp_rpc_reply_error(AMQP_STATUS_NO_MEMORY);
1066
0
      }
1067
1068
0
      *frame_copy = frame;
1069
1070
0
      link->next = NULL;
1071
0
      link->data = frame_copy;
1072
1073
0
      if (state->last_queued_frame == NULL) {
1074
0
        state->first_queued_frame = link;
1075
0
      } else {
1076
0
        state->last_queued_frame->next = link;
1077
0
      }
1078
0
      state->last_queued_frame = link;
1079
1080
0
      goto retry;
1081
0
    }
1082
1083
0
    result.reply_type =
1084
0
        (amqp_id_in_reply_list(frame.payload.method.id, expected_reply_ids))
1085
0
            ? AMQP_RESPONSE_NORMAL
1086
0
            : AMQP_RESPONSE_SERVER_EXCEPTION;
1087
1088
0
    result.reply = frame.payload.method;
1089
0
    return result;
1090
0
  }
1091
0
}
1092
1093
amqp_rpc_reply_t amqp_simple_rpc(amqp_connection_state_t state,
1094
                                 amqp_channel_t channel,
1095
                                 amqp_method_number_t request_id,
1096
                                 amqp_method_number_t *expected_reply_ids,
1097
0
                                 void *decoded_request_method) {
1098
0
  amqp_time_t deadline;
1099
0
  int res;
1100
1101
0
  res = amqp_time_from_now(&deadline, state->rpc_timeout);
1102
0
  if (res != AMQP_STATUS_OK) {
1103
0
    return amqp_rpc_reply_error(res);
1104
0
  }
1105
1106
0
  return simple_rpc_inner(state, channel, request_id, expected_reply_ids,
1107
0
                          decoded_request_method, deadline);
1108
0
}
1109
1110
void *amqp_simple_rpc_decoded(amqp_connection_state_t state,
1111
                              amqp_channel_t channel,
1112
                              amqp_method_number_t request_id,
1113
                              amqp_method_number_t reply_id,
1114
0
                              void *decoded_request_method) {
1115
0
  amqp_time_t deadline;
1116
0
  int res;
1117
0
  amqp_method_number_t replies[2];
1118
1119
0
  res = amqp_time_from_now(&deadline, state->rpc_timeout);
1120
0
  if (res != AMQP_STATUS_OK) {
1121
0
    state->most_recent_api_result = amqp_rpc_reply_error(res);
1122
0
    return NULL;
1123
0
  }
1124
1125
0
  replies[0] = reply_id;
1126
0
  replies[1] = 0;
1127
1128
0
  state->most_recent_api_result = simple_rpc_inner(
1129
0
      state, channel, request_id, replies, decoded_request_method, deadline);
1130
1131
0
  if (state->most_recent_api_result.reply_type == AMQP_RESPONSE_NORMAL) {
1132
0
    return state->most_recent_api_result.reply.decoded;
1133
0
  } else {
1134
0
    return NULL;
1135
0
  }
1136
0
}
1137
1138
0
amqp_rpc_reply_t amqp_get_rpc_reply(amqp_connection_state_t state) {
1139
0
  return state->most_recent_api_result;
1140
0
}
1141
1142
/*
1143
 * Merge base and add tables. If the two tables contain an entry with the same
1144
 * key, the entry from the add table takes precedence. For entries that are both
1145
 * tables with the same key, the table is recursively merged.
1146
 */
1147
int amqp_merge_capabilities(const amqp_table_t *base, const amqp_table_t *add,
1148
0
                            amqp_table_t *result, amqp_pool_t *pool) {
1149
0
  int i;
1150
0
  int res;
1151
0
  amqp_pool_t temp_pool;
1152
0
  amqp_table_t temp_result;
1153
0
  assert(base != NULL);
1154
0
  assert(result != NULL);
1155
0
  assert(pool != NULL);
1156
1157
0
  if (NULL == add) {
1158
0
    return amqp_table_clone(base, result, pool);
1159
0
  }
1160
1161
0
  init_amqp_pool(&temp_pool, 4096);
1162
0
  temp_result.num_entries = 0;
1163
0
  temp_result.entries =
1164
0
      amqp_pool_alloc(&temp_pool, sizeof(amqp_table_entry_t) *
1165
0
                                      (base->num_entries + add->num_entries));
1166
0
  if (NULL == temp_result.entries) {
1167
0
    res = AMQP_STATUS_NO_MEMORY;
1168
0
    goto error_out;
1169
0
  }
1170
0
  for (i = 0; i < base->num_entries; ++i) {
1171
0
    temp_result.entries[temp_result.num_entries] = base->entries[i];
1172
0
    temp_result.num_entries++;
1173
0
  }
1174
0
  for (i = 0; i < add->num_entries; ++i) {
1175
0
    amqp_table_entry_t *e =
1176
0
        amqp_table_get_entry_by_key(&temp_result, add->entries[i].key);
1177
0
    if (NULL != e) {
1178
0
      if (AMQP_FIELD_KIND_TABLE == add->entries[i].value.kind &&
1179
0
          AMQP_FIELD_KIND_TABLE == e->value.kind) {
1180
0
        amqp_table_entry_t *be =
1181
0
            amqp_table_get_entry_by_key(base, add->entries[i].key);
1182
1183
0
        res = amqp_merge_capabilities(&be->value.value.table,
1184
0
                                      &add->entries[i].value.value.table,
1185
0
                                      &e->value.value.table, &temp_pool);
1186
0
        if (AMQP_STATUS_OK != res) {
1187
0
          goto error_out;
1188
0
        }
1189
0
      } else {
1190
0
        e->value = add->entries[i].value;
1191
0
      }
1192
0
    } else {
1193
0
      temp_result.entries[temp_result.num_entries] = add->entries[i];
1194
0
      temp_result.num_entries++;
1195
0
    }
1196
0
  }
1197
0
  res = amqp_table_clone(&temp_result, result, pool);
1198
0
error_out:
1199
0
  empty_amqp_pool(&temp_pool);
1200
0
  return res;
1201
0
}
1202
1203
static amqp_rpc_reply_t amqp_login_inner(amqp_connection_state_t state,
1204
                                         char const *vhost, int channel_max,
1205
                                         int frame_max, int heartbeat,
1206
                                         const amqp_table_t *client_properties,
1207
                                         const struct timeval *timeout,
1208
                                         amqp_sasl_method_enum sasl_method,
1209
44
                                         va_list vl) {
1210
44
  int res;
1211
44
  amqp_method_t method;
1212
1213
44
  uint16_t client_channel_max;
1214
44
  uint32_t client_frame_max;
1215
44
  uint16_t client_heartbeat;
1216
1217
44
  uint16_t server_channel_max;
1218
44
  uint32_t server_frame_max;
1219
44
  uint16_t server_heartbeat;
1220
1221
44
  amqp_rpc_reply_t result;
1222
44
  amqp_time_t deadline;
1223
1224
44
  if (channel_max < 0 || channel_max > UINT16_MAX) {
1225
0
    return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
1226
0
  }
1227
44
  client_channel_max = (uint16_t)channel_max;
1228
1229
44
  if (frame_max < 0) {
1230
0
    return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
1231
0
  }
1232
44
  client_frame_max = (uint32_t)frame_max;
1233
1234
44
  if (heartbeat < 0 || heartbeat > UINT16_MAX) {
1235
0
    return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
1236
0
  }
1237
44
  client_heartbeat = (uint16_t)heartbeat;
1238
1239
44
  res = amqp_time_from_now(&deadline, timeout);
1240
44
  if (AMQP_STATUS_OK != res) {
1241
0
    goto error_res;
1242
0
  }
1243
1244
44
  res = send_header_inner(state, deadline);
1245
44
  if (AMQP_STATUS_OK != res) {
1246
0
    goto error_res;
1247
0
  }
1248
1249
44
  res = simple_wait_method_inner(state, 0, AMQP_CONNECTION_START_METHOD,
1250
44
                                 deadline, &method);
1251
44
  if (AMQP_STATUS_OK != res) {
1252
44
    goto error_res;
1253
44
  }
1254
1255
0
  {
1256
0
    amqp_connection_start_t *s = (amqp_connection_start_t *)method.decoded;
1257
0
    if ((s->version_major != AMQP_PROTOCOL_VERSION_MAJOR) ||
1258
0
        (s->version_minor != AMQP_PROTOCOL_VERSION_MINOR)) {
1259
0
      res = AMQP_STATUS_INCOMPATIBLE_AMQP_VERSION;
1260
0
      goto error_res;
1261
0
    }
1262
1263
0
    res = amqp_table_clone(&s->server_properties, &state->server_properties,
1264
0
                           &state->properties_pool);
1265
1266
0
    if (AMQP_STATUS_OK != res) {
1267
0
      goto error_res;
1268
0
    }
1269
1270
    /* TODO: check that our chosen SASL mechanism is in the list of
1271
       acceptable mechanisms. Or even let the application choose from
1272
       the list! */
1273
0
    if (!sasl_mechanism_in_list(s->mechanisms, sasl_method)) {
1274
0
      res = AMQP_STATUS_BROKER_UNSUPPORTED_SASL_METHOD;
1275
0
      goto error_res;
1276
0
    }
1277
0
  }
1278
1279
0
  {
1280
0
    amqp_table_entry_t default_properties[6];
1281
0
    amqp_table_t default_table;
1282
0
    amqp_table_entry_t client_capabilities[2];
1283
0
    amqp_table_t client_capabilities_table;
1284
0
    amqp_connection_start_ok_t s;
1285
0
    amqp_pool_t *channel_pool;
1286
0
    amqp_bytes_t response_bytes;
1287
1288
0
    channel_pool = amqp_get_or_create_channel_pool(state, 0);
1289
0
    if (NULL == channel_pool) {
1290
0
      res = AMQP_STATUS_NO_MEMORY;
1291
0
      goto error_res;
1292
0
    }
1293
1294
0
    response_bytes = sasl_response(channel_pool, sasl_method, vl);
1295
0
    if (response_bytes.bytes == NULL) {
1296
0
      res = AMQP_STATUS_NO_MEMORY;
1297
0
      goto error_res;
1298
0
    }
1299
1300
0
    client_capabilities[0] =
1301
0
        amqp_table_construct_bool_entry("authentication_failure_close", 1);
1302
0
    client_capabilities[1] =
1303
0
        amqp_table_construct_bool_entry("exchange_exchange_bindings", 1);
1304
1305
0
    client_capabilities_table.entries = client_capabilities;
1306
0
    client_capabilities_table.num_entries =
1307
0
        sizeof(client_capabilities) / sizeof(amqp_table_entry_t);
1308
1309
0
    default_properties[0] =
1310
0
        amqp_table_construct_utf8_entry("product", "rabbitmq-c");
1311
0
    default_properties[1] =
1312
0
        amqp_table_construct_utf8_entry("version", AMQP_VERSION_STRING);
1313
0
    default_properties[2] =
1314
0
        amqp_table_construct_utf8_entry("platform", AMQ_PLATFORM);
1315
0
    default_properties[3] =
1316
0
        amqp_table_construct_utf8_entry("copyright", AMQ_COPYRIGHT);
1317
0
    default_properties[4] = amqp_table_construct_utf8_entry(
1318
0
        "information", "See https://github.com/alanxz/rabbitmq-c");
1319
0
    default_properties[5] = amqp_table_construct_table_entry(
1320
0
        "capabilities", &client_capabilities_table);
1321
1322
0
    default_table.entries = default_properties;
1323
0
    default_table.num_entries =
1324
0
        sizeof(default_properties) / sizeof(amqp_table_entry_t);
1325
1326
0
    res = amqp_merge_capabilities(&default_table, client_properties,
1327
0
                                  &state->client_properties, channel_pool);
1328
0
    if (AMQP_STATUS_OK != res) {
1329
0
      goto error_res;
1330
0
    }
1331
1332
0
    s.client_properties = state->client_properties;
1333
0
    s.mechanism = sasl_method_name(sasl_method);
1334
0
    s.response = response_bytes;
1335
0
    s.locale = amqp_literal_bytes("en_US");
1336
1337
0
    res = amqp_send_method_inner(state, 0, AMQP_CONNECTION_START_OK_METHOD, &s,
1338
0
                                 AMQP_SF_NONE, deadline);
1339
0
    if (res < 0) {
1340
0
      goto error_res;
1341
0
    }
1342
0
  }
1343
1344
0
  amqp_release_buffers(state);
1345
1346
0
  {
1347
0
    amqp_method_number_t expected[] = {AMQP_CONNECTION_TUNE_METHOD,
1348
0
                                       AMQP_CONNECTION_CLOSE_METHOD, 0};
1349
1350
0
    res = amqp_simple_wait_method_list(state, 0, expected, deadline, &method);
1351
0
    if (AMQP_STATUS_OK != res) {
1352
0
      goto error_res;
1353
0
    }
1354
0
  }
1355
1356
0
  if (AMQP_CONNECTION_CLOSE_METHOD == method.id) {
1357
0
    result.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION;
1358
0
    result.reply = method;
1359
0
    result.library_error = 0;
1360
0
    goto out;
1361
0
  }
1362
1363
0
  {
1364
0
    amqp_connection_tune_t *s = (amqp_connection_tune_t *)method.decoded;
1365
0
    server_channel_max = s->channel_max;
1366
0
    server_frame_max = s->frame_max;
1367
0
    server_heartbeat = s->heartbeat;
1368
0
  }
1369
1370
0
  if (server_channel_max != 0 &&
1371
0
      (server_channel_max < client_channel_max || client_channel_max == 0)) {
1372
0
    client_channel_max = server_channel_max;
1373
0
  } else if (server_channel_max == 0 && client_channel_max == 0) {
1374
0
    client_channel_max = UINT16_MAX;
1375
0
  }
1376
1377
0
  if (server_frame_max != 0 && server_frame_max < client_frame_max) {
1378
0
    client_frame_max = server_frame_max;
1379
0
  }
1380
1381
0
  if (server_heartbeat != 0 && server_heartbeat < client_heartbeat) {
1382
0
    client_heartbeat = server_heartbeat;
1383
0
  }
1384
1385
0
  res = amqp_tune_connection(state, client_channel_max, client_frame_max,
1386
0
                             client_heartbeat);
1387
0
  if (res < 0) {
1388
0
    goto error_res;
1389
0
  }
1390
0
  client_frame_max = (uint32_t)amqp_get_frame_max(state);
1391
1392
0
  {
1393
0
    amqp_connection_tune_ok_t s;
1394
0
    s.frame_max = client_frame_max;
1395
0
    s.channel_max = client_channel_max;
1396
0
    s.heartbeat = client_heartbeat;
1397
1398
0
    res = amqp_send_method_inner(state, 0, AMQP_CONNECTION_TUNE_OK_METHOD, &s,
1399
0
                                 AMQP_SF_NONE, deadline);
1400
0
    if (res < 0) {
1401
0
      goto error_res;
1402
0
    }
1403
0
  }
1404
1405
0
  amqp_release_buffers(state);
1406
1407
0
  {
1408
0
    amqp_method_number_t replies[] = {AMQP_CONNECTION_OPEN_OK_METHOD, 0};
1409
0
    amqp_connection_open_t s;
1410
0
    s.virtual_host = amqp_cstring_bytes(vhost);
1411
0
    s.capabilities = amqp_empty_bytes;
1412
0
    s.insist = 1;
1413
1414
0
    result = simple_rpc_inner(state, 0, AMQP_CONNECTION_OPEN_METHOD, replies,
1415
0
                              &s, deadline);
1416
0
    if (result.reply_type != AMQP_RESPONSE_NORMAL) {
1417
0
      goto out;
1418
0
    }
1419
0
  }
1420
1421
0
  result.reply_type = AMQP_RESPONSE_NORMAL;
1422
0
  result.reply.id = 0;
1423
0
  result.reply.decoded = NULL;
1424
0
  result.library_error = 0;
1425
0
  amqp_maybe_release_buffers(state);
1426
1427
44
out:
1428
44
  return result;
1429
1430
44
error_res:
1431
44
  amqp_socket_close(state->socket, AMQP_SC_FORCE);
1432
44
  result = amqp_rpc_reply_error(res);
1433
1434
44
  goto out;
1435
0
}
1436
1437
amqp_rpc_reply_t amqp_login(amqp_connection_state_t state, char const *vhost,
1438
                            int channel_max, int frame_max, int heartbeat,
1439
44
                            amqp_sasl_method_enum sasl_method, ...) {
1440
1441
44
  va_list vl;
1442
44
  amqp_rpc_reply_t ret;
1443
1444
44
  va_start(vl, sasl_method);
1445
1446
44
  ret = amqp_login_inner(state, vhost, channel_max, frame_max, heartbeat,
1447
44
                         &amqp_empty_table, state->handshake_timeout,
1448
44
                         sasl_method, vl);
1449
1450
44
  va_end(vl);
1451
1452
44
  return ret;
1453
44
}
1454
1455
amqp_rpc_reply_t amqp_login_with_properties(
1456
    amqp_connection_state_t state, char const *vhost, int channel_max,
1457
    int frame_max, int heartbeat, const amqp_table_t *client_properties,
1458
0
    amqp_sasl_method_enum sasl_method, ...) {
1459
0
  va_list vl;
1460
0
  amqp_rpc_reply_t ret;
1461
1462
0
  va_start(vl, sasl_method);
1463
1464
0
  ret = amqp_login_inner(state, vhost, channel_max, frame_max, heartbeat,
1465
0
                         client_properties, state->handshake_timeout,
1466
0
                         sasl_method, vl);
1467
1468
0
  va_end(vl);
1469
1470
0
  return ret;
1471
0
}