/** Send PING */ int tport_ws_ping(tport_t *self, su_time_t now) { ssize_t n; char *why = ""; if (tport_has_queued(self)) return 0; n = send(self->tp_socket, "\r\n\r\n", 4, 0); if (n > 0) self->tp_ktime = now; if (n == 4) { if (self->tp_ptime.tv_sec == 0) self->tp_ptime = now; } else if (n == -1) { int error = su_errno(); why = " failed"; if (!su_is_blocking(error)) tport_error_report(self, error, NULL); else why = " blocking"; return -1; } SU_DEBUG_7(("%s(%p): %s to " TPN_FORMAT "%s\n", __func__, (void *)self, "sending PING", TPN_ARGS(self->tp_name), why)); return n == -1 ? -1 : 0; }
/** Process UDP error event. */ int tport_udp_error(tport_t const *self, su_sockaddr_t name[1]) { struct cmsghdr *c; struct sock_extended_err *ee; su_sockaddr_t *from; char control[512]; char errmsg[64 + 768]; struct iovec iov[1]; struct msghdr msg[1] = {{ 0 }}; int n; msg->msg_name = name, msg->msg_namelen = sizeof(*name); msg->msg_iov = iov, msg->msg_iovlen = 1; iov->iov_base = errmsg, iov->iov_len = sizeof(errmsg); msg->msg_control = control, msg->msg_controllen = sizeof(control); n = recvmsg(self->tp_socket, msg, MSG_ERRQUEUE); if (n < 0) { int err = su_errno(); if (!su_is_blocking(err)) SU_DEBUG_1(("%s: recvmsg: %s\n", __func__, su_strerror(err))); return 0; } if ((msg->msg_flags & MSG_ERRQUEUE) != MSG_ERRQUEUE) { SU_DEBUG_1(("%s: recvmsg: no errqueue\n", __func__)); return 0; } if (msg->msg_flags & MSG_CTRUNC) { SU_DEBUG_1(("%s: extended error was truncated\n", __func__)); return 0; } if (msg->msg_flags & MSG_TRUNC) { /* ICMP message may contain original message... */ SU_DEBUG_3(("%s: icmp(6) message was truncated (at %d)\n", __func__, n)); } /* Go through the ancillary data */ for (c = CMSG_FIRSTHDR(msg); c; c = CMSG_NXTHDR(msg, c)) { if (0 #if HAVE_IP_RECVERR || (c->cmsg_level == IPPROTO_IP && c->cmsg_type == IP_RECVERR) #endif #if HAVE_IPV6_RECVERR || (c->cmsg_level == IPPROTO_IPV6 && c->cmsg_type == IPV6_RECVERR) #endif ) { char info[128]; char const *origin; ee = (struct sock_extended_err *)CMSG_DATA(c); from = (su_sockaddr_t *)SO_EE_OFFENDER(ee); info[0] = '\0'; switch (ee->ee_origin) { case SO_EE_ORIGIN_LOCAL: origin = "local"; break; case SO_EE_ORIGIN_ICMP: origin = "icmp"; snprintf(info, sizeof(info), " type=%u code=%u", ee->ee_type, ee->ee_code); break; case SO_EE_ORIGIN_ICMP6: origin = "icmp6"; snprintf(info, sizeof(info), " type=%u code=%u", ee->ee_type, ee->ee_code); break; case SO_EE_ORIGIN_NONE: origin = "none"; break; default: origin = "unknown"; break; } if (ee->ee_info) snprintf(info + strlen(info), sizeof(info) - strlen(info), " info=%08x", ee->ee_info); SU_DEBUG_3(("%s: %s (%d) [%s%s]\n", __func__, su_strerror(ee->ee_errno), ee->ee_errno, origin, info)); if (from->su_family != AF_UNSPEC) SU_DEBUG_3(("\treported by [%s]:%u\n", su_inet_ntop(from->su_family, SU_ADDR(from), info, sizeof(info)), ntohs(from->su_port))); if (msg->msg_namelen == 0) name->su_family = AF_UNSPEC; SU_CANONIZE_SOCKADDR(name); return ee->ee_errno; } } return 0; }
/** Send to stream */ ssize_t tport_send_stream_ws(tport_t const *self, msg_t *msg, msg_iovec_t iov[], size_t iovlen) { size_t i, j, n, m, size = 0; ssize_t nerror; tport_ws_t *wstp = (tport_ws_t *)self; enum { WSBUFSIZE = 2048 }; for (i = 0; i < iovlen; i = j) { char *buf = wstp->wstp_buffer; unsigned wsbufsize = WSBUFSIZE; if (i + 1 == iovlen) { buf = NULL; /* Don't bother copying single chunk */ } if (buf && (char *)iov[i].siv_base - buf < WSBUFSIZE && (char *)iov[i].siv_base - buf >= 0) { wsbufsize = buf + WSBUFSIZE - (char *)iov[i].siv_base; assert(wsbufsize <= WSBUFSIZE); } for (j = i, m = 0; buf && j < iovlen; j++) { if (m + iov[j].siv_len > wsbufsize) { break; } if (buf + m != iov[j].siv_base) { memcpy(buf + m, iov[j].siv_base, iov[j].siv_len); } m += iov[j].siv_len; iov[j].siv_len = 0; } if (j == i) { buf = iov[i].siv_base, m = iov[i].siv_len, j++; } else { iov[j].siv_base = buf, iov[j].siv_len = m; } nerror = ws_feed_buf(&wstp->ws, buf, m); SU_DEBUG_9(("tport_ws_writevec: vec %p %p %lu ("MOD_ZD")\n", (void *)&wstp->ws, (void *)iov[i].siv_base, (LU)iov[i].siv_len, nerror)); if (nerror == -1) { int err = su_errno(); if (su_is_blocking(err)) break; SU_DEBUG_3(("ws_write: %s\n", strerror(err))); return -1; } n = (size_t)nerror; size += n; /* Return if the write buffer is full for now */ if (n != m) break; } ws_send_buf(&wstp->ws, WSOC_TEXT); return size; }
/** 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; }