Coverage Report

Created: 2026-10-17 06:33

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
8.54k
                         int flags) {
91
8.54k
  assert(self);
92
8.54k
  assert(self->klass->send);
93
8.54k
  return self->klass->send(self, buf, len, flags);
94
8.54k
}
95
96
ssize_t amqp_socket_recv(amqp_socket_t *self, void *buf, size_t len,
97
14.1k
                         int flags) {
98
14.1k
  assert(self);
99
14.1k
  assert(self->klass->recv);
100
14.1k
  return self->klass->recv(self, buf, len, flags);
101
14.1k
}
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
4.17k
int amqp_socket_close(amqp_socket_t *self, amqp_socket_close_enum force) {
117
4.17k
  assert(self);
118
4.17k
  assert(self->klass->close);
119
4.17k
  return self->klass->close(self, force);
120
4.17k
}
121
122
26.7k
void amqp_socket_delete(amqp_socket_t *self) {
123
26.7k
  if (self) {
124
10.8k
    assert(self->klass->delete);
125
10.8k
    self->klass->delete (self);
126
10.8k
  }
127
26.7k
}
128
129
3.10k
int amqp_socket_get_sockfd(amqp_socket_t *self) {
130
3.10k
  assert(self);
131
3.10k
  assert(self->klass->get_sockfd);
132
3.10k
  return self->klass->get_sockfd(self);
133
3.10k
}
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
8.54k
                      size_t len, amqp_time_t deadline, int flags) {
251
8.54k
  ssize_t res;
252
8.54k
  void *buf_left = (void *)buf;
253
  /* Assume that len is not going to be larger than ssize_t can hold. */
254
8.54k
  ssize_t len_left = (size_t)len;
255
256
8.54k
start_send:
257
8.54k
  res = amqp_socket_send(state->socket, buf_left, len_left, flags);
258
259
8.54k
  if (res > 0) {
260
8.54k
    len_left -= res;
261
8.54k
    buf_left = (char *)buf_left + res;
262
8.54k
    if (0 == len_left) {
263
8.54k
      return (ssize_t)len;
264
8.54k
    }
265
0
    goto start_send;
266
8.54k
  }
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
5.25k
                             amqp_time_t deadline) {
487
5.25k
  ssize_t res;
488
5.25k
  static const uint8_t header[8] = {'A',
489
5.25k
                                    'M',
490
5.25k
                                    'Q',
491
5.25k
                                    'P',
492
5.25k
                                    0,
493
5.25k
                                    AMQP_PROTOCOL_VERSION_MAJOR,
494
5.25k
                                    AMQP_PROTOCOL_VERSION_MINOR,
495
5.25k
                                    AMQP_PROTOCOL_VERSION_REVISION};
496
5.25k
  res = amqp_try_send(state, header, sizeof(header), deadline, AMQP_SF_NONE);
497
5.25k
  if (sizeof(header) == res) {
498
5.25k
    return AMQP_STATUS_OK;
499
5.25k
  }
500
0
  return (int)res;
501
5.25k
}
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
2.45k
static amqp_bytes_t sasl_method_name(amqp_sasl_method_enum method) {
508
2.45k
  amqp_bytes_t res;
509
510
2.45k
  switch (method) {
511
2.45k
    case AMQP_SASL_METHOD_PLAIN:
512
2.45k
      res = amqp_literal_bytes("PLAIN");
513
2.45k
      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
2.45k
  }
521
522
2.45k
  return res;
523
2.45k
}
524
525
8.90k
static int bytes_equal(amqp_bytes_t l, amqp_bytes_t r) {
526
8.90k
  if (l.len == r.len) {
527
1.39k
    if (l.bytes && r.bytes) {
528
1.39k
      if (0 == memcmp(l.bytes, r.bytes, l.len)) {
529
1.12k
        return 1;
530
1.12k
      }
531
1.39k
    }
532
1.39k
  }
533
7.77k
  return 0;
534
8.90k
}
535
536
int sasl_mechanism_in_list(amqp_bytes_t mechanisms,
537
1.32k
                           amqp_sasl_method_enum method) {
538
1.32k
  amqp_bytes_t mechanism;
539
1.32k
  amqp_bytes_t supported_mechanism;
540
1.32k
  uint8_t *start;
541
1.32k
  uint8_t *end;
542
1.32k
  uint8_t *current;
543
544
1.32k
  assert(NULL != mechanisms.bytes);
545
546
1.32k
  mechanism = sasl_method_name(method);
547
548
1.32k
  start = (uint8_t *)mechanisms.bytes;
549
1.32k
  current = start;
550
1.32k
  end = start + mechanisms.len;
551
552
9.10k
  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
8.90k
    current = memchr(start, ' ', end - start);
558
8.90k
    if (NULL == current) {
559
59
      current = end;
560
59
    }
561
8.90k
    supported_mechanism.bytes = start;
562
8.90k
    supported_mechanism.len = current - start;
563
8.90k
    if (bytes_equal(mechanism, supported_mechanism)) {
564
1.12k
      return 1;
565
1.12k
    }
566
8.90k
  }
567
568
199
  return 0;
569
1.32k
}
570
571
static amqp_bytes_t sasl_response(amqp_pool_t *pool,
572
                                  amqp_sasl_method_enum method, va_list args,
573
1.12k
                                  int *status) {
574
1.12k
  amqp_bytes_t response = amqp_empty_bytes;
575
576
1.12k
  *status = AMQP_STATUS_OK;
577
578
1.12k
  switch (method) {
579
1.12k
    case AMQP_SASL_METHOD_PLAIN: {
580
1.12k
      char *username = va_arg(args, char *);
581
1.12k
      char *password = va_arg(args, char *);
582
1.12k
      size_t username_len;
583
1.12k
      size_t password_len;
584
1.12k
      char *response_buf;
585
586
1.12k
      if (username == NULL || password == NULL) {
587
0
        *status = AMQP_STATUS_INVALID_PARAMETER;
588
0
        return response;
589
0
      }
590
591
1.12k
      username_len = strlen(username);
592
1.12k
      password_len = strlen(password);
593
594
1.12k
      amqp_pool_alloc_bytes(pool, username_len + password_len + 2, &response);
595
1.12k
      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
1.12k
      response_buf = response.bytes;
604
1.12k
      response_buf[0] = 0;
605
1.12k
      memcpy(response_buf + 1, username, username_len);
606
1.12k
      response_buf[username_len + 1] = 0;
607
1.12k
      memcpy(response_buf + username_len + 2, password, password_len);
608
1.12k
      break;
609
1.12k
    }
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
1.12k
  }
642
643
1.12k
  return response;
644
1.12k
}
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
692k
amqp_boolean_t amqp_data_in_buffer(amqp_connection_state_t state) {
655
692k
  return (state->sock_inbound_offset < state->sock_inbound_limit);
656
692k
}
657
658
static int consume_one_frame(amqp_connection_state_t state,
659
678k
                             amqp_frame_t *decoded_frame) {
660
678k
  int res;
661
662
678k
  amqp_bytes_t buffer;
663
678k
  buffer.len = state->sock_inbound_limit - state->sock_inbound_offset;
664
678k
  buffer.bytes =
665
678k
      ((char *)state->sock_inbound_buffer.bytes) + state->sock_inbound_offset;
666
667
678k
  res = amqp_handle_input(state, buffer, decoded_frame);
668
678k
  if (res < 0) {
669
6.43k
    return res;
670
6.43k
  }
671
672
671k
  state->sock_inbound_offset += res;
673
674
671k
  return AMQP_STATUS_OK;
675
678k
}
676
677
static int recv_with_timeout(amqp_connection_state_t state,
678
14.1k
                             amqp_time_t timeout) {
679
14.1k
  ssize_t res;
680
14.1k
  int fd;
681
682
14.1k
start_recv:
683
14.1k
  res = amqp_socket_recv(state->socket, state->sock_inbound_buffer.bytes,
684
14.1k
                         state->sock_inbound_buffer.len, 0);
685
686
14.1k
  if (res < 0) {
687
3.10k
    fd = amqp_get_sockfd(state);
688
3.10k
    if (-1 == fd) {
689
0
      return AMQP_STATUS_CONNECTION_CLOSED;
690
0
    }
691
3.10k
    switch (res) {
692
3.10k
      default:
693
3.10k
        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
3.10k
    }
701
0
    if (AMQP_STATUS_OK == res) {
702
0
      goto start_recv;
703
0
    }
704
0
    return (int)res;
705
0
  }
706
707
11.0k
  state->sock_inbound_limit = res;
708
11.0k
  state->sock_inbound_offset = 0;
709
710
11.0k
  res = amqp_time_s_from_now(&state->next_recv_heartbeat,
711
11.0k
                             amqp_heartbeat_recv(state));
712
11.0k
  if (AMQP_STATUS_OK != res) {
713
0
    return (int)res;
714
0
  }
715
11.0k
  return AMQP_STATUS_OK;
716
11.0k
}
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
676k
                            amqp_time_t timeout_deadline) {
771
676k
  amqp_time_t deadline;
772
676k
  int res;
773
774
687k
  for (;;) {
775
692k
    while (amqp_data_in_buffer(state)) {
776
678k
      res = consume_one_frame(state, decoded_frame);
777
778
678k
      if (AMQP_STATUS_OK != res) {
779
6.43k
        return res;
780
6.43k
      }
781
782
671k
      if (AMQP_FRAME_HEARTBEAT == decoded_frame->frame_type) {
783
4.12k
        amqp_maybe_release_buffers_on_channel(state, 0);
784
4.12k
        continue;
785
4.12k
      }
786
787
667k
      if (decoded_frame->frame_type != 0) {
788
        /* Complete frame was read. Return it. */
789
666k
        return AMQP_STATUS_OK;
790
666k
      }
791
667k
    }
792
793
14.1k
  beginrecv:
794
14.1k
    res = amqp_time_has_past(state->next_send_heartbeat);
795
14.1k
    if (AMQP_STATUS_TIMER_FAILURE == res) {
796
0
      return res;
797
14.1k
    } 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
14.1k
    deadline = amqp_time_first(timeout_deadline,
808
14.1k
                               amqp_time_first(state->next_recv_heartbeat,
809
14.1k
                                               state->next_send_heartbeat));
810
811
    /* TODO this needs to wait for a _frame_ and not anything written from the
812
     * socket */
813
14.1k
    res = recv_with_timeout(state, deadline);
814
815
14.1k
    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
14.1k
    } else if (AMQP_STATUS_OK != res) {
828
3.10k
      return res;
829
3.10k
    }
830
14.1k
  }
831
676k
}
832
833
static amqp_link_t *amqp_create_link_for_frame(amqp_connection_state_t state,
834
376k
                                               amqp_frame_t *frame) {
835
376k
  amqp_link_t *link;
836
376k
  amqp_frame_t *frame_copy;
837
838
376k
  amqp_pool_t *channel_pool =
839
376k
      amqp_get_or_create_channel_pool(state, frame->channel);
840
841
376k
  if (NULL == channel_pool) {
842
0
    return NULL;
843
0
  }
844
845
376k
  link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t));
846
376k
  frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t));
847
848
376k
  if (NULL == link || NULL == frame_copy) {
849
0
    return NULL;
850
0
  }
851
852
376k
  *frame_copy = *frame;
853
376k
  link->data = frame_copy;
854
855
376k
  return link;
856
376k
}
857
858
376k
int amqp_queue_frame(amqp_connection_state_t state, amqp_frame_t *frame) {
859
376k
  amqp_link_t *link = amqp_create_link_for_frame(state, frame);
860
376k
  if (NULL == link) {
861
0
    return AMQP_STATUS_NO_MEMORY;
862
0
  }
863
864
376k
  if (NULL == state->first_queued_frame) {
865
1.86k
    state->first_queued_frame = link;
866
374k
  } else {
867
374k
    state->last_queued_frame->next = link;
868
374k
  }
869
870
376k
  link->next = NULL;
871
376k
  state->last_queued_frame = link;
872
873
376k
  return AMQP_STATUS_OK;
874
376k
}
875
876
37
int amqp_put_back_frame(amqp_connection_state_t state, amqp_frame_t *frame) {
877
37
  amqp_link_t *link = amqp_create_link_for_frame(state, frame);
878
37
  if (NULL == link) {
879
0
    return AMQP_STATUS_NO_MEMORY;
880
0
  }
881
882
37
  if (NULL == state->first_queued_frame) {
883
32
    state->first_queued_frame = link;
884
32
    state->last_queued_frame = link;
885
32
    link->next = NULL;
886
32
  } else {
887
5
    link->next = state->first_queued_frame;
888
5
    state->first_queued_frame = link;
889
5
  }
890
891
37
  return AMQP_STATUS_OK;
892
37
}
893
894
int amqp_simple_wait_frame_on_channel(amqp_connection_state_t state,
895
                                      amqp_channel_t channel,
896
6.52k
                                      amqp_frame_t *decoded_frame) {
897
6.52k
  amqp_frame_t *frame_ptr;
898
6.52k
  amqp_link_t *cur;
899
6.52k
  int res;
900
901
6.85k
  for (cur = state->first_queued_frame; NULL != cur; cur = cur->next) {
902
332
    frame_ptr = cur->data;
903
904
332
    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
332
  }
915
916
382k
  for (;;) {
917
382k
    res = wait_frame_inner(state, decoded_frame, amqp_time_infinite());
918
919
382k
    if (AMQP_STATUS_OK != res) {
920
5.44k
      return res;
921
5.44k
    }
922
923
377k
    if (channel == decoded_frame->channel) {
924
1.07k
      return AMQP_STATUS_OK;
925
376k
    } else {
926
376k
      res = amqp_queue_frame(state, decoded_frame);
927
376k
      if (res != AMQP_STATUS_OK) {
928
0
        return res;
929
0
      }
930
376k
    }
931
377k
  }
932
6.52k
}
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
6.38k
                                   const struct timeval *timeout) {
942
6.38k
  amqp_time_t deadline;
943
944
6.38k
  int res = amqp_time_from_now(&deadline, timeout);
945
6.38k
  if (AMQP_STATUS_OK != res) {
946
0
    return res;
947
0
  }
948
949
6.38k
  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
6.38k
  } else {
958
6.38k
    return wait_frame_inner(state, decoded_frame, deadline);
959
6.38k
  }
960
6.38k
}
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
6.38k
                                        amqp_method_t *output) {
967
6.38k
  amqp_frame_t frame;
968
6.38k
  struct timeval tv;
969
6.38k
  struct timeval *tvp;
970
971
6.38k
  int res = amqp_time_tv_until(deadline, &tv, &tvp);
972
6.38k
  if (res != AMQP_STATUS_OK) {
973
0
    return res;
974
0
  }
975
976
6.38k
  res = amqp_simple_wait_frame_noblock(state, &frame, tvp);
977
6.38k
  if (AMQP_STATUS_OK != res) {
978
3.06k
    return res;
979
3.06k
  }
980
981
3.31k
  if (AMQP_FRAME_METHOD != frame.frame_type ||
982
3.19k
      expected_channel != frame.channel ||
983
2.72k
      !amqp_id_in_reply_list(frame.payload.method.id, expected_methods)) {
984
868
    return AMQP_STATUS_WRONG_METHOD;
985
868
  }
986
2.45k
  *output = frame.payload.method;
987
2.45k
  return AMQP_STATUS_OK;
988
3.31k
}
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
5.25k
                                    amqp_method_t *output) {
995
5.25k
  amqp_method_number_t expected_methods[2];
996
5.25k
  expected_methods[0] = expected_method;
997
5.25k
  expected_methods[1] = 0;
998
5.25k
  return amqp_simple_wait_method_list(state, expected_channel, expected_methods,
999
5.25k
                                      deadline, output);
1000
5.25k
}
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
1.08k
                     amqp_method_number_t id, void *decoded) {
1012
1.08k
  return amqp_send_method_inner(state, channel, id, decoded, AMQP_SF_NONE,
1013
1.08k
                                amqp_time_infinite());
1014
1.08k
}
1015
1016
int amqp_send_method_inner(amqp_connection_state_t state,
1017
                           amqp_channel_t channel, amqp_method_number_t id,
1018
3.28k
                           void *decoded, int flags, amqp_time_t deadline) {
1019
3.28k
  amqp_frame_t frame;
1020
1021
3.28k
  frame.frame_type = AMQP_FRAME_METHOD;
1022
3.28k
  frame.channel = channel;
1023
3.28k
  frame.payload.method.id = id;
1024
3.28k
  frame.payload.method.decoded = decoded;
1025
3.28k
  return amqp_send_frame_inner(state, &frame, flags, deadline);
1026
3.28k
}
1027
1028
static int amqp_id_in_reply_list(amqp_method_number_t expected,
1029
131k
                                 amqp_method_number_t *list) {
1030
259k
  while (*list != 0) {
1031
131k
    if (*list == expected) {
1032
2.54k
      return 1;
1033
2.54k
    }
1034
128k
    list++;
1035
128k
  }
1036
128k
  return 0;
1037
131k
}
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
1.08k
    void *decoded_request_method, amqp_time_t deadline) {
1043
1.08k
  int status;
1044
1.08k
  amqp_rpc_reply_t result;
1045
1046
1.08k
  memset(&result, 0, sizeof(result));
1047
1048
1.08k
  status = amqp_send_method(state, channel, request_id, decoded_request_method);
1049
1.08k
  if (status < 0) {
1050
0
    return amqp_rpc_reply_error(status);
1051
0
  }
1052
1053
1.08k
  {
1054
1.08k
    amqp_frame_t frame;
1055
1056
287k
  retry:
1057
287k
    status = wait_frame_inner(state, &frame, deadline);
1058
287k
    if (status != AMQP_STATUS_OK) {
1059
1.02k
      if (status == AMQP_STATUS_TIMEOUT) {
1060
0
        amqp_socket_close(state->socket, AMQP_SC_FORCE);
1061
0
      }
1062
1.02k
      return amqp_rpc_reply_error(status);
1063
1.02k
    }
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
286k
    if (!((frame.frame_type == AMQP_FRAME_METHOD) &&
1074
139k
          (((frame.channel == channel) &&
1075
128k
            (amqp_id_in_reply_list(frame.payload.method.id,
1076
128k
                                   expected_reply_ids) ||
1077
128k
             (frame.payload.method.id == AMQP_CHANNEL_CLOSE_METHOD))) ||
1078
139k
           ((frame.channel == 0) &&
1079
286k
            (frame.payload.method.id == AMQP_CONNECTION_CLOSE_METHOD))))) {
1080
286k
      amqp_pool_t *channel_pool;
1081
286k
      amqp_frame_t *frame_copy;
1082
286k
      amqp_link_t *link;
1083
1084
286k
      channel_pool = amqp_get_or_create_channel_pool(state, frame.channel);
1085
286k
      if (NULL == channel_pool) {
1086
0
        return amqp_rpc_reply_error(AMQP_STATUS_NO_MEMORY);
1087
0
      }
1088
1089
286k
      frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t));
1090
286k
      link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t));
1091
1092
286k
      if (frame_copy == NULL || link == NULL) {
1093
0
        return amqp_rpc_reply_error(AMQP_STATUS_NO_MEMORY);
1094
0
      }
1095
1096
286k
      *frame_copy = frame;
1097
1098
286k
      link->next = NULL;
1099
286k
      link->data = frame_copy;
1100
1101
286k
      if (state->last_queued_frame == NULL) {
1102
907
        state->first_queued_frame = link;
1103
285k
      } else {
1104
285k
        state->last_queued_frame->next = link;
1105
285k
      }
1106
286k
      state->last_queued_frame = link;
1107
1108
286k
      goto retry;
1109
286k
    }
1110
1111
54
    result.reply_type =
1112
54
        (amqp_id_in_reply_list(frame.payload.method.id, expected_reply_ids))
1113
54
            ? AMQP_RESPONSE_NORMAL
1114
54
            : AMQP_RESPONSE_SERVER_EXCEPTION;
1115
1116
54
    result.reply = frame.payload.method;
1117
54
    return result;
1118
286k
  }
1119
286k
}
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
1.12k
                            amqp_table_t *result, amqp_pool_t *pool) {
1177
1.12k
  int i;
1178
1.12k
  int res;
1179
1.12k
  amqp_pool_t temp_pool;
1180
1.12k
  amqp_table_t temp_result;
1181
1.12k
  assert(base != NULL);
1182
1.12k
  assert(result != NULL);
1183
1.12k
  assert(pool != NULL);
1184
1185
1.12k
  if (NULL == add) {
1186
0
    return amqp_table_clone(base, result, pool);
1187
0
  }
1188
1189
1.12k
  init_amqp_pool(&temp_pool, 4096);
1190
1.12k
  temp_result.num_entries = 0;
1191
1.12k
  temp_result.entries =
1192
1.12k
      amqp_pool_alloc(&temp_pool, sizeof(amqp_table_entry_t) *
1193
1.12k
                                      (base->num_entries + add->num_entries));
1194
1.12k
  if (NULL == temp_result.entries) {
1195
0
    res = AMQP_STATUS_NO_MEMORY;
1196
0
    goto error_out;
1197
0
  }
1198
7.89k
  for (i = 0; i < base->num_entries; ++i) {
1199
6.76k
    temp_result.entries[temp_result.num_entries] = base->entries[i];
1200
6.76k
    temp_result.num_entries++;
1201
6.76k
  }
1202
1.12k
  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
1.12k
  res = amqp_table_clone(&temp_result, result, pool);
1226
1.12k
error_out:
1227
1.12k
  empty_amqp_pool(&temp_pool);
1228
1.12k
  return res;
1229
1.12k
}
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
5.25k
                                         va_list vl) {
1238
5.25k
  int res;
1239
5.25k
  amqp_method_t method;
1240
1241
5.25k
  uint16_t client_channel_max;
1242
5.25k
  uint32_t client_frame_max;
1243
5.25k
  uint16_t client_heartbeat;
1244
1245
5.25k
  uint16_t server_channel_max;
1246
5.25k
  uint32_t server_frame_max;
1247
5.25k
  uint16_t server_heartbeat;
1248
1249
5.25k
  amqp_rpc_reply_t result;
1250
5.25k
  amqp_time_t deadline;
1251
1252
5.25k
  if (channel_max < 0 || channel_max > UINT16_MAX) {
1253
0
    return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
1254
0
  }
1255
5.25k
  client_channel_max = (uint16_t)channel_max;
1256
1257
5.25k
  if (frame_max < 0) {
1258
0
    return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
1259
0
  }
1260
5.25k
  client_frame_max = (uint32_t)frame_max;
1261
1262
5.25k
  if (heartbeat < 0 || heartbeat > UINT16_MAX) {
1263
0
    return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
1264
0
  }
1265
5.25k
  client_heartbeat = (uint16_t)heartbeat;
1266
1267
5.25k
  res = amqp_time_from_now(&deadline, timeout);
1268
5.25k
  if (AMQP_STATUS_OK != res) {
1269
0
    goto error_res;
1270
0
  }
1271
1272
5.25k
  res = send_header_inner(state, deadline);
1273
5.25k
  if (AMQP_STATUS_OK != res) {
1274
0
    goto error_res;
1275
0
  }
1276
1277
5.25k
  res = simple_wait_method_inner(state, 0, AMQP_CONNECTION_START_METHOD,
1278
5.25k
                                 deadline, &method);
1279
5.25k
  if (AMQP_STATUS_OK != res) {
1280
3.88k
    goto error_res;
1281
3.88k
  }
1282
1283
1.36k
  {
1284
1.36k
    amqp_connection_start_t *s = (amqp_connection_start_t *)method.decoded;
1285
1.36k
    if ((s->version_major != AMQP_PROTOCOL_VERSION_MAJOR) ||
1286
1.35k
        (s->version_minor != AMQP_PROTOCOL_VERSION_MINOR)) {
1287
21
      res = AMQP_STATUS_INCOMPATIBLE_AMQP_VERSION;
1288
21
      goto error_res;
1289
21
    }
1290
1291
1.34k
    res = amqp_table_clone(&s->server_properties, &state->server_properties,
1292
1.34k
                           &state->properties_pool);
1293
1294
1.34k
    if (AMQP_STATUS_OK != res) {
1295
21
      goto error_res;
1296
21
    }
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
1.32k
    if (!sasl_mechanism_in_list(s->mechanisms, sasl_method)) {
1302
199
      res = AMQP_STATUS_BROKER_UNSUPPORTED_SASL_METHOD;
1303
199
      goto error_res;
1304
199
    }
1305
1.32k
  }
1306
1307
1.12k
  {
1308
1.12k
    amqp_table_entry_t default_properties[6];
1309
1.12k
    amqp_table_t default_table;
1310
1.12k
    amqp_table_entry_t client_capabilities[2];
1311
1.12k
    amqp_table_t client_capabilities_table;
1312
1.12k
    amqp_connection_start_ok_t s;
1313
1.12k
    amqp_pool_t *channel_pool;
1314
1.12k
    amqp_bytes_t response_bytes;
1315
1316
1.12k
    channel_pool = amqp_get_or_create_channel_pool(state, 0);
1317
1.12k
    if (NULL == channel_pool) {
1318
0
      res = AMQP_STATUS_NO_MEMORY;
1319
0
      goto error_res;
1320
0
    }
1321
1322
1.12k
    response_bytes = sasl_response(channel_pool, sasl_method, vl, &res);
1323
1.12k
    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
1.12k
    client_capabilities[0] =
1332
1.12k
        amqp_table_construct_bool_entry("authentication_failure_close", 1);
1333
1.12k
    client_capabilities[1] =
1334
1.12k
        amqp_table_construct_bool_entry("exchange_exchange_bindings", 1);
1335
1336
1.12k
    client_capabilities_table.entries = client_capabilities;
1337
1.12k
    client_capabilities_table.num_entries =
1338
1.12k
        sizeof(client_capabilities) / sizeof(amqp_table_entry_t);
1339
1340
1.12k
    default_properties[0] =
1341
1.12k
        amqp_table_construct_utf8_entry("product", "rabbitmq-c");
1342
1.12k
    default_properties[1] =
1343
1.12k
        amqp_table_construct_utf8_entry("version", AMQP_VERSION_STRING);
1344
1.12k
    default_properties[2] =
1345
1.12k
        amqp_table_construct_utf8_entry("platform", AMQ_PLATFORM);
1346
1.12k
    default_properties[3] =
1347
1.12k
        amqp_table_construct_utf8_entry("copyright", AMQ_COPYRIGHT);
1348
1.12k
    default_properties[4] = amqp_table_construct_utf8_entry(
1349
1.12k
        "information", "See https://github.com/alanxz/rabbitmq-c");
1350
1.12k
    default_properties[5] = amqp_table_construct_table_entry(
1351
1.12k
        "capabilities", &client_capabilities_table);
1352
1353
1.12k
    default_table.entries = default_properties;
1354
1.12k
    default_table.num_entries =
1355
1.12k
        sizeof(default_properties) / sizeof(amqp_table_entry_t);
1356
1357
1.12k
    res = amqp_merge_capabilities(&default_table, client_properties,
1358
1.12k
                                  &state->client_properties, channel_pool);
1359
1.12k
    if (AMQP_STATUS_OK != res) {
1360
0
      goto error_res;
1361
0
    }
1362
1363
1.12k
    s.client_properties = state->client_properties;
1364
1.12k
    s.mechanism = sasl_method_name(sasl_method);
1365
1.12k
    s.response = response_bytes;
1366
1.12k
    s.locale = amqp_literal_bytes("en_US");
1367
1368
1.12k
    res = amqp_send_method_inner(state, 0, AMQP_CONNECTION_START_OK_METHOD, &s,
1369
1.12k
                                 AMQP_SF_NONE, deadline);
1370
1.12k
    if (res < 0) {
1371
0
      goto error_res;
1372
0
    }
1373
1.12k
  }
1374
1375
1.12k
  amqp_release_buffers(state);
1376
1377
1.12k
  {
1378
1.12k
    amqp_method_number_t expected[] = {AMQP_CONNECTION_TUNE_METHOD,
1379
1.12k
                                       AMQP_CONNECTION_CLOSE_METHOD, 0};
1380
1381
1.12k
    res = amqp_simple_wait_method_list(state, 0, expected, deadline, &method);
1382
1.12k
    if (AMQP_STATUS_OK != res) {
1383
46
      goto error_res;
1384
46
    }
1385
1.12k
  }
1386
1387
1.08k
  if (AMQP_CONNECTION_CLOSE_METHOD == method.id) {
1388
2
    result.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION;
1389
2
    result.reply = method;
1390
2
    result.library_error = 0;
1391
2
    goto out;
1392
2
  }
1393
1394
1.08k
  {
1395
1.08k
    amqp_connection_tune_t *s = (amqp_connection_tune_t *)method.decoded;
1396
1.08k
    server_channel_max = s->channel_max;
1397
1.08k
    server_frame_max = s->frame_max;
1398
1.08k
    server_heartbeat = s->heartbeat;
1399
1.08k
  }
1400
1401
1.08k
  if (server_channel_max != 0 &&
1402
868
      (server_channel_max < client_channel_max || client_channel_max == 0)) {
1403
868
    client_channel_max = server_channel_max;
1404
868
  } else if (server_channel_max == 0 && client_channel_max == 0) {
1405
212
    client_channel_max = UINT16_MAX;
1406
212
  }
1407
1408
1.08k
  if (server_frame_max != 0 && server_frame_max < client_frame_max) {
1409
307
    client_frame_max = server_frame_max;
1410
307
  }
1411
1412
1.08k
  if (server_heartbeat != 0 && server_heartbeat < client_heartbeat) {
1413
0
    client_heartbeat = server_heartbeat;
1414
0
  }
1415
1416
1.08k
  res = amqp_tune_connection(state, client_channel_max, client_frame_max,
1417
1.08k
                             client_heartbeat);
1418
1.08k
  if (res < 0) {
1419
0
    goto error_res;
1420
0
  }
1421
1.08k
  client_frame_max = (uint32_t)amqp_get_frame_max(state);
1422
1423
1.08k
  {
1424
1.08k
    amqp_connection_tune_ok_t s;
1425
1.08k
    s.frame_max = client_frame_max;
1426
1.08k
    s.channel_max = client_channel_max;
1427
1.08k
    s.heartbeat = client_heartbeat;
1428
1429
1.08k
    res = amqp_send_method_inner(state, 0, AMQP_CONNECTION_TUNE_OK_METHOD, &s,
1430
1.08k
                                 AMQP_SF_NONE, deadline);
1431
1.08k
    if (res < 0) {
1432
0
      goto error_res;
1433
0
    }
1434
1.08k
  }
1435
1436
1.08k
  amqp_release_buffers(state);
1437
1438
1.08k
  {
1439
1.08k
    amqp_method_number_t replies[] = {AMQP_CONNECTION_OPEN_OK_METHOD, 0};
1440
1.08k
    amqp_connection_open_t s;
1441
1.08k
    s.virtual_host = amqp_cstring_bytes(vhost);
1442
1.08k
    s.capabilities = amqp_empty_bytes;
1443
1.08k
    s.insist = 1;
1444
1445
1.08k
    result = simple_rpc_inner(state, 0, AMQP_CONNECTION_OPEN_METHOD, replies,
1446
1.08k
                              &s, deadline);
1447
1.08k
    if (result.reply_type != AMQP_RESPONSE_NORMAL) {
1448
1.03k
      goto out;
1449
1.03k
    }
1450
1.08k
  }
1451
1452
48
  result.reply_type = AMQP_RESPONSE_NORMAL;
1453
48
  result.reply.id = 0;
1454
48
  result.reply.decoded = NULL;
1455
48
  result.library_error = 0;
1456
48
  amqp_maybe_release_buffers(state);
1457
1458
5.25k
out:
1459
5.25k
  return result;
1460
1461
4.17k
error_res:
1462
4.17k
  amqp_socket_close(state->socket, AMQP_SC_FORCE);
1463
4.17k
  result = amqp_rpc_reply_error(res);
1464
1465
4.17k
  goto out;
1466
48
}
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
5.25k
                            amqp_sasl_method_enum sasl_method, ...) {
1471
1472
5.25k
  va_list vl;
1473
5.25k
  amqp_rpc_reply_t ret;
1474
1475
5.25k
  va_start(vl, sasl_method);
1476
1477
5.25k
  ret = amqp_login_inner(state, vhost, channel_max, frame_max, heartbeat,
1478
5.25k
                         &amqp_empty_table, state->handshake_timeout,
1479
5.25k
                         sasl_method, vl);
1480
1481
5.25k
  va_end(vl);
1482
1483
5.25k
  return ret;
1484
5.25k
}
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
}