/** Receive from stream. * * @retval -1 error * @retval 0 end-of-stream * @retval 1 normal receive * @retval 2 incomplete recv, recv again * */ int tport_recv_stream_ws(tport_t *self) { msg_t *msg; ssize_t n, N, veclen, i, m; int err; msg_iovec_t iovec[msg_n_fragments] = {{ 0 }}; tport_ws_t *wstp = (tport_ws_t *)self; uint8_t *data; ws_opcode_t oc; if (wstp->ws_initialized < 0) { return -1; } N = ws_read_frame(&wstp->ws, &oc, &data); if (N == -2) { return 2; } if ((N == -1000) || (N == 0)) { if (self->tp_msg) { msg_recv_commit(self->tp_msg, 0, 1); } return 0; /* End of stream */ } if (N < 0) { err = errno = EHOSTDOWN; SU_DEBUG_1(("%s(%p): su_getmsgsize(): %s (%d)\n", __func__, (void *)self, su_strerror(err), err)); return 0; } veclen = tport_recv_iovec(self, &self->tp_msg, iovec, N, 0); if (veclen < 0) return -1; msg = self->tp_msg; msg_set_address(msg, self->tp_addr, self->tp_addrlen); for (i = 0, n = 0; i < veclen; i++) { m = iovec[i].mv_len; assert(N >= n + m); memcpy(iovec[i].mv_base, data + n, m); n += m; } assert(N == n); /* Write the received data to the message dump file */ if (self->tp_master->mr_dump_file) tport_dump_iovec(self, msg, n, iovec, veclen, "recv", "from"); /* Mark buffer as used */ msg_recv_commit(msg, N, 0); return 1; }
/** Receive from stream. * * @retval -1 error * @retval 0 end-of-stream * @retval 1 normal receive * @retval 2 incomplete recv, recv again * */ int tport_recv_stream(tport_t *self) { msg_t *msg; ssize_t n, N, veclen; int err, initial; msg_iovec_t iovec[msg_n_fragments] = {{ 0 }}; N = su_getmsgsize(self->tp_socket); if (N == 0) { if (self->tp_msg) msg_recv_commit(self->tp_msg, 0, 1); return 0; /* End of stream */ } if (N == -1) { err = su_errno(); SU_DEBUG_1(("%s(%p): su_getmsgsize(): %s (%d)\n", __func__, (void *)self, su_strerror(err), err)); return -1; } initial = self->tp_msg == NULL; memset(&self->tp_ptime, 0, sizeof self->tp_ptime); while (initial && N <= 8) { /* Check for whitespace */ char crlf[9]; size_t i; n = su_recv(self->tp_socket, crlf, N, MSG_PEEK); i = ws_span(crlf, n); if (i == 0) break; n = su_recv(self->tp_socket, crlf, i, 0); if (n <= 0) return (int)n; SU_DEBUG_7(("%s(%p): received keepalive (total %u)\n", __func__, (void *)self, self->tp_ping)); N -= n, self->tp_ping += n; tport_recv_bytes(self, n, n); if (N == 0) { /* outbound-10 section 3.5.1 - send pong */ if (self->tp_ping >= 4) tport_tcp_pong(self); return 1; } } veclen = tport_recv_iovec(self, &self->tp_msg, iovec, N, 0); if (veclen == -1) return -1; msg = self->tp_msg; msg_set_address(msg, self->tp_addr, (socklen_t)(self->tp_addrlen)); n = su_vrecv(self->tp_socket, iovec, veclen, 0, NULL, NULL); if (n == SOCKET_ERROR) return tport_recv_error_report(self); assert(n <= N); tport_recv_bytes(self, n, n); /* Check if message contains only whitespace */ /* This can happen if multiple PINGs are received at once */ if (initial) { size_t i = ws_span(iovec->siv_base, iovec->siv_len); if (i + self->tp_ping >= 4) tport_tcp_pong(self); else self->tp_ping += (unsigned short)i; if (i == iovec->siv_len && veclen == 1) { SU_DEBUG_7(("%s(%p): received %u bytes of keepalive\n", __func__, (void *)self, (unsigned)i)); msg_destroy(self->tp_msg), self->tp_msg = NULL; return 1; } } /* Write the received data to the message dump file */ if (self->tp_master->mr_dump_file) tport_dump_iovec(self, msg, n, iovec, veclen, "recv", "from"); if (self->tp_master->mr_capt_sock) tport_capt_msg(self, msg, n, iovec, veclen, "recv"); /* Mark buffer as used */ msg_recv_commit(msg, n, n == 0); if (n > 0) self->tp_ping = 0; return n != 0; }
/** Receive datagram. * * @retval -1 error * @retval 0 end-of-stream * @retval 1 normal receive (should never happen) * @retval 2 incomplete recv, call me again (should never happen) * @retval 3 STUN keepalive, ignore */ int tport_recv_dgram(tport_t *self) { msg_t *msg; ssize_t n, veclen, N; su_addrinfo_t *ai; su_sockaddr_t *from; socklen_t fromlen; msg_iovec_t iovec[msg_n_fragments] = {{ 0 }}; uint8_t sample[1]; /* Simulate packet loss */ if (self->tp_params->tpp_drop && (unsigned)su_randint(0, 1000) < self->tp_params->tpp_drop) { su_recv(self->tp_socket, sample, 1, 0); SU_DEBUG_3(("tport(%p): simulated packet loss!\n", (void *)self)); return 0; } assert(self->tp_msg == NULL); #if nomore /* We used to resize the buffer, but it fragments the memory */ N = 65535; #else N = (ssize_t)su_getmsgsize(self->tp_socket); if (N == -1) { int err = su_errno(); SU_DEBUG_1(("%s(%p): su_getmsgsize(): %s (%d)\n", __func__, (void *)self, su_strerror(err), err)); return -1; } if (N == 0) { su_recv(self->tp_socket, sample, 1, 0); SU_DEBUG_3(("tport(%p): zero length packet", (void *)self)); return 0; } #endif veclen = tport_recv_iovec(self, &self->tp_msg, iovec, N, 1); if (veclen == -1) return -1; msg = self->tp_msg; ai = msg_addrinfo(msg); from = (su_sockaddr_t *)ai->ai_addr, fromlen = (socklen_t)(ai->ai_addrlen); n = su_vrecv(self->tp_socket, iovec, veclen, 0, from, &fromlen); ai->ai_addrlen = fromlen; if (n == SOCKET_ERROR) { int error = su_errno(); msg_destroy(msg); self->tp_msg = NULL; su_seterrno(error); if (su_is_blocking(error)) return 0; else return -1; } else if (n <= 1) { SU_DEBUG_1(("%s(%p): runt of "MOD_ZD" bytes\n", "tport_recv_dgram", (void *)self, n)); msg_destroy(msg), self->tp_msg = NULL; return 0; } tport_recv_bytes(self, n, n); SU_CANONIZE_SOCKADDR(from); if (self->tp_master->mr_dump_file) tport_dump_iovec(self, msg, n, iovec, veclen, "recv", "from"); *sample = *((uint8_t *)iovec[0].mv_base); /* Commit received data into buffer. This may relocate iovec contents */ msg_recv_commit(msg, n, 1); if ((sample[0] & 0xf8) == 0xf8) /* SigComp */ return tport_recv_comp_dgram(self, self->tp_comp, &self->tp_msg, from, fromlen); #if HAVE_SOFIA_STUN else if (sample[0] == 0 || sample[0] == 1) /* STUN request or response */ return tport_recv_stun_dgram(self, &self->tp_msg, from, fromlen); #endif else return 0; }