summaryrefslogtreecommitdiff
path: root/src/common/net/unix.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/net/unix.h')
-rw-r--r--src/common/net/unix.h120
1 files changed, 40 insertions, 80 deletions
diff --git a/src/common/net/unix.h b/src/common/net/unix.h
index 920ca87..a5ad510 100644
--- a/src/common/net/unix.h
+++ b/src/common/net/unix.h
@@ -25,36 +25,12 @@ static const char *os_error_string(int err)
return strerror(err);
}
-// Receiving ICMP errors on unconnected UDP sockets is tricky...
-// Linux 2.2 and higher supports this via IP_RECVERR option, see ip(7).
-// What about BSD?
-
-#if USE_ICMP && (defined __linux__)
-
-static qboolean check_offender(const struct sockaddr_in *from,
- const struct sockaddr_in *to)
-{
- if (!to)
- return qfalse;
-
- if (from->sin_addr.s_addr != to->sin_addr.s_addr)
- return qfalse;
-
- if (from->sin_port && from->sin_port != to->sin_port)
- return qfalse;
-
- Com_DPrintf("%s: found offending address %s:%d\n", "process_error_queue",
- inet_ntoa(from->sin_addr), ntohs(from->sin_port));
-
- return qtrue;
-}
-
-// Returns true if failed socket operation should be retried.
-// May get called from NET_SendPacket() path, avoid interfering with net_from.
-static qboolean process_error_queue(netsrc_t sock, const struct sockaddr_in *to_addr)
+// returns true if failed socket operation should be retried.
+static qboolean process_error_queue(qsocket_t sock, const netadr_t *to)
{
+#ifdef IP_RECVERR
byte buffer[1024];
- struct sockaddr_in from_addr;
+ struct sockaddr_storage from_addr;
struct msghdr msg;
struct cmsghdr *cmsg;
struct sock_extended_err *ee;
@@ -71,7 +47,7 @@ static qboolean process_error_queue(netsrc_t sock, const struct sockaddr_in *to_
msg.msg_control = buffer;
msg.msg_controllen = sizeof(buffer);
- if (recvmsg(udp_sockets[sock], &msg, MSG_ERRQUEUE) == -1) {
+ if (recvmsg(sock, &msg, MSG_ERRQUEUE) == -1) {
if (errno != EWOULDBLOCK)
Com_DPrintf("%s: %s\n", __func__, strerror(errno));
break;
@@ -86,14 +62,17 @@ static qboolean process_error_queue(netsrc_t sock, const struct sockaddr_in *to_
for (cmsg = CMSG_FIRSTHDR(&msg);
cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
- if (cmsg->cmsg_level != IPPROTO_IP) {
+ if (cmsg->cmsg_level != IPPROTO_IP &&
+ cmsg->cmsg_level != IPPROTO_IPV6) {
continue;
}
- if (cmsg->cmsg_type != IP_RECVERR) {
+ if (cmsg->cmsg_type != IP_RECVERR &&
+ cmsg->cmsg_type != IPV6_RECVERR) {
continue;
}
ee = (struct sock_extended_err *)CMSG_DATA(cmsg);
- if (ee->ee_origin == SO_EE_ORIGIN_ICMP) {
+ if (ee->ee_origin == SO_EE_ORIGIN_ICMP ||
+ ee->ee_origin == SO_EE_ORIGIN_ICMP6) {
break;
}
}
@@ -103,33 +82,38 @@ static qboolean process_error_queue(netsrc_t sock, const struct sockaddr_in *to_
break;
}
- found |= check_offender(&from_addr, to_addr);
-
NET_SockadrToNetadr(&from_addr, &from);
+ // check for offender address being current packet destination
+ if (to != NULL && NET_IsEqualBaseAdr(&from, to) &&
+ (from.port == 0 || from.port == to->port)) {
+ Com_DPrintf("%s: found offending address: %s\n", __func__,
+ NET_AdrToString(&from));
+ found = qtrue;
+ }
+
// handle ICMP error
NET_ErrorEvent(sock, &from, ee->ee_errno, ee->ee_info);
}
return !!tries && !found;
+#else
+ return qfalse;
+#endif
}
-#endif // !__linux__
-
-static ssize_t os_udp_recv(netsrc_t sock, void *data,
+static ssize_t os_udp_recv(qsocket_t sock, void *data,
size_t len, netadr_t *from)
{
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
socklen_t addrlen;
ssize_t ret;
-
-#if USE_ICMP && (defined __linux__)
int tries;
+
for (tries = 0; tries < MAX_ERROR_RETRIES; tries++) {
-#endif
memset(&addr, 0, sizeof(addr));
addrlen = sizeof(addr);
- ret = recvfrom(udp_sockets[sock], data, len, 0,
+ ret = recvfrom(sock, data, len, 0,
(struct sockaddr *)&addr, &addrlen);
NET_SockadrToNetadr(&addr, from);
@@ -143,32 +127,26 @@ static ssize_t os_udp_recv(netsrc_t sock, void *data,
if (net_error == EWOULDBLOCK)
return NET_AGAIN;
-#if USE_ICMP && (defined __linux__)
- // recvfrom() fails on Linux if there is an ICMP originated pending
- // error on socket. Suck up error queue and retry...
-
if (!process_error_queue(sock, NULL))
break;
}
-#endif
return NET_ERROR;
}
-static ssize_t os_udp_send(netsrc_t sock, const void *data,
+static ssize_t os_udp_send(qsocket_t sock, const void *data,
size_t len, const netadr_t *to)
{
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
ssize_t ret;
+ int tries;
- NET_NetadrToSockadr(to, &addr);
+ addrlen = NET_NetadrToSockadr(to, &addr);
-#if USE_ICMP && (defined __linux__)
- int tries;
for (tries = 0; tries < MAX_ERROR_RETRIES; tries++) {
-#endif
- ret = sendto(udp_sockets[sock], data, len, 0,
- (struct sockaddr *)&addr, sizeof(addr));
+ ret = sendto(sock, data, len, 0,
+ (struct sockaddr *)&addr, addrlen);
if (ret >= 0)
return ret;
@@ -178,28 +156,9 @@ static ssize_t os_udp_send(netsrc_t sock, const void *data,
if (net_error == EWOULDBLOCK)
return NET_AGAIN;
-#if USE_ICMP && (defined __linux__)
- // sendto() fails on Linux if there is an ICMP originated pending error
- // on socket. Suck up error queue and retry...
- //
- // But how do I distingiush between a failure caused by completely
- // unrelated ICMP error sitting in the queue and an error directly
- // related to this sendto() call?
- //
- // On one hand, I don't want to drop packets to legitimate clients, and
- // have to retry sendto() after processing error queue. On other
- // hand, infinite loop should be avoided if sendto() call regenerates
- // the message in error queue.
- //
- // This mess is worked around by passing destination address to
- // process_error_queue() and checking if this address/port pair is
- // found in the queue. If it is found, or the queue is empty, do not
- // retry.
-
- if (!process_error_queue(sock, &addr))
+ if (!process_error_queue(sock, to))
break;
}
-#endif
return NET_ERROR;
}
@@ -245,7 +204,7 @@ static neterr_t os_listen(qsocket_t sock, int backlog)
static neterr_t os_accept(qsocket_t sock, qsocket_t *newsock, netadr_t *from)
{
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
socklen_t addrlen;
int s;
@@ -266,11 +225,12 @@ static neterr_t os_accept(qsocket_t sock, qsocket_t *newsock, netadr_t *from)
static neterr_t os_connect(qsocket_t sock, const netadr_t *to)
{
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
- NET_NetadrToSockadr(to, &addr);
+ addrlen = NET_NetadrToSockadr(to, &addr);
- if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ if (connect(sock, (struct sockaddr *)&addr, addrlen) == -1) {
net_error = errno;
if (net_error == EINPROGRESS)
return NET_OK;
@@ -325,7 +285,7 @@ static neterr_t os_bind(qsocket_t sock, const struct sockaddr *addr, size_t addr
static neterr_t os_getsockname(qsocket_t sock, netadr_t *name)
{
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
socklen_t addrlen;
memset(&addr, 0, sizeof(addr));