+ if(sendto(listen_socket[sock].udp.fd, (void *)SEQNO(inpkt), inpkt->len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
+ if(sockmsgsize(sockerrno)) {
+ if(n->maxmtu >= origlen)
+ n->maxmtu = origlen - 1;
+ if(n->mtu >= origlen)
+ n->mtu = origlen - 1;
+ try_fix_mtu(n);
+ } else
+ logger(DEBUG_TRAFFIC, LOG_WARNING, "Error sending packet to %s (%s): %s", n->name, n->hostname, sockstrerror(sockerrno));
+ }
+
+end:
+ origpkt->len = origlen;
+#endif
+}
+
+bool send_sptps_data(node_t *to, node_t *from, int type, const void *data, size_t len) {
+ node_t *relay = (to->via != myself && (type == PKT_PROBE || (len - SPTPS_DATAGRAM_OVERHEAD) <= to->via->minmtu)) ? to->via : to->nexthop;
+ bool direct = from == myself && to == relay;
+ bool relay_supported = (relay->options >> 24) >= 4;
+ bool tcponly = (myself->options | relay->options) & OPTION_TCPONLY;
+
+ /* Send it via TCP if it is a handshake packet, TCPOnly is in use, this is a relay packet that the other node cannot understand, or this packet is larger than the MTU. */
+
+ if(type == SPTPS_HANDSHAKE || tcponly || (!direct && !relay_supported) || (type != PKT_PROBE && (len - SPTPS_DATAGRAM_OVERHEAD) > relay->minmtu)) {
+ if(type != SPTPS_HANDSHAKE && (to->nexthop->connection->options >> 24) >= 7) {
+ char buf[len + sizeof to->id + sizeof from->id]; char* buf_ptr = buf;
+ memcpy(buf_ptr, &to->id, sizeof to->id); buf_ptr += sizeof to->id;
+ memcpy(buf_ptr, &from->id, sizeof from->id); buf_ptr += sizeof from->id;
+ memcpy(buf_ptr, data, len); buf_ptr += len;
+ logger(DEBUG_TRAFFIC, LOG_INFO, "Sending packet from %s (%s) to %s (%s) via %s (%s) (TCP)", from->name, from->hostname, to->name, to->hostname, to->nexthop->name, to->nexthop->hostname);
+ return send_sptps_tcppacket(to->nexthop->connection, buf, sizeof buf);
+ }
+
+ char buf[len * 4 / 3 + 5];
+ b64encode(data, buf, len);
+ /* If this is a handshake packet, use ANS_KEY instead of REQ_KEY, for two reasons:
+ - We don't want intermediate nodes to switch to UDP to relay these packets;
+ - ANS_KEY allows us to learn the reflexive UDP address. */
+ if(type == SPTPS_HANDSHAKE) {
+ to->incompression = myself->incompression;
+ return send_request(to->nexthop->connection, "%d %s %s %s -1 -1 -1 %d", ANS_KEY, from->name, to->name, buf, to->incompression);
+ } else {
+ return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, from->name, to->name, SPTPS_PACKET, buf);
+ }
+ }
+
+ size_t overhead = 0;
+ if(relay_supported) overhead += sizeof to->id + sizeof from->id;
+ char buf[len + overhead]; char* buf_ptr = buf;
+ if(relay_supported) {
+ if(direct) {
+ /* Inform the recipient that this packet was sent directly. */
+ node_id_t nullid = {};
+ memcpy(buf_ptr, &nullid, sizeof nullid); buf_ptr += sizeof nullid;
+ } else {
+ memcpy(buf_ptr, &to->id, sizeof to->id); buf_ptr += sizeof to->id;
+ }
+ memcpy(buf_ptr, &from->id, sizeof from->id); buf_ptr += sizeof from->id;
+
+ }
+ /* TODO: if this copy turns out to be a performance concern, change sptps_send_record() to add some "pre-padding" to the buffer and use that instead */
+ memcpy(buf_ptr, data, len); buf_ptr += len;
+
+ const sockaddr_t *sa = NULL;
+ int sock;
+ if(relay->status.send_locally)
+ choose_local_address(relay, &sa, &sock);
+ if(!sa)
+ choose_udp_address(relay, &sa, &sock);
+ logger(DEBUG_TRAFFIC, LOG_INFO, "Sending packet from %s (%s) to %s (%s) via %s (%s) (UDP)", from->name, from->hostname, to->name, to->hostname, relay->name, relay->hostname);
+ if(sendto(listen_socket[sock].udp.fd, buf, buf_ptr - buf, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
+ if(sockmsgsize(sockerrno)) {
+ // Compensate for SPTPS overhead
+ len -= SPTPS_DATAGRAM_OVERHEAD;
+ if(relay->maxmtu >= len)
+ relay->maxmtu = len - 1;
+ if(relay->mtu >= len)
+ relay->mtu = len - 1;
+ try_fix_mtu(relay);
+ } else {
+ logger(DEBUG_TRAFFIC, LOG_WARNING, "Error sending UDP SPTPS packet to %s (%s): %s", relay->name, relay->hostname, sockstrerror(sockerrno));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t len) {
+ node_t *from = handle;
+
+ if(type == SPTPS_HANDSHAKE) {
+ if(!from->status.validkey) {
+ from->status.validkey = true;
+ from->status.waitingforkey = false;
+ logger(DEBUG_META, LOG_INFO, "SPTPS key exchange with %s (%s) succesful", from->name, from->hostname);
+ }
+ return true;
+ }
+
+ if(len > MTU) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Packet from %s (%s) larger than maximum supported size (%d > %d)", from->name, from->hostname, len, MTU);
+ return false;
+ }
+
+ vpn_packet_t inpkt;
+ inpkt.offset = DEFAULT_PACKET_OFFSET;
+
+ if(type == PKT_PROBE) {
+ if(!from->status.udppacket) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Got SPTPS PROBE packet from %s (%s) via TCP", from->name, from->hostname);
+ return false;
+ }
+ inpkt.len = len;
+ memcpy(DATA(&inpkt), data, len);
+ if(inpkt.len > from->maxrecentlen)
+ from->maxrecentlen = inpkt.len;
+ udp_probe_h(from, &inpkt, len);
+ return true;
+ }