summaryrefslogtreecommitdiffstats
path: root/noport.c
blob: 22088eb042b97f1040a16e025bd5dce48e3315af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#define _GNU_SOURCE
#include <sys/socket.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>

static int
port_is_okay(unsigned short port)
{
	char *p, *q;
	long l;

	p = getenv("NOPORT");
	while ((p != NULL) && (*p != '\0')) {
		l = strtol(p, &q, 10);
		if ((q == NULL) || (q == p)) {
			break;
		}
		if ((*q == '\0') || (*q == ',')) {
			if (port == l) {
				errno = ECONNREFUSED;
				return -1;
			}
		}
		p = q;
		p += strspn(p, ",");
	}
	return 0;
}

int
connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
	unsigned short port;
	static int (*next_connect)(int, const struct sockaddr *, socklen_t);

	if (next_connect == NULL) {
		next_connect = dlsym(RTLD_NEXT, "connect");
		if (next_connect == NULL) {
			errno = ENOSYS;
			return -1;
		}
	}

	if (getenv("NOPORT") == NULL) {
		return next_connect(sockfd, addr, addrlen);
	}

	switch (addr->sa_family) {
	case AF_INET:
		port = ntohs(((struct sockaddr_in *)addr)->sin_port);
		if (port_is_okay(port) != 0) {
			return -1;
		}
		break;
	case AF_INET6:
		port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
		if (port_is_okay(port) != 0) {
			return -1;
		}
		break;
	default:
		break;
	}
	return next_connect(sockfd, addr, addrlen);
}

ssize_t
sendto(int sockfd, const void *buf, size_t len, int flags,
       const struct sockaddr *dest_addr, socklen_t addrlen)
{
	unsigned short port;
	static int (*next_sendto)(int, const void *, size_t, int,
				  const struct sockaddr *, socklen_t);

	if (next_sendto == NULL) {
		next_sendto = dlsym(RTLD_NEXT, "sendto");
		if (next_sendto == NULL) {
			errno = ENOSYS;
			return -1;
		}
	}

	if (getenv("NOPORT") == NULL) {
		return next_sendto(sockfd, buf, len, flags, dest_addr, addrlen);
	}

	if (dest_addr != NULL) {
		switch (dest_addr->sa_family) {
		case AF_INET:
			port = ((struct sockaddr_in *)dest_addr)->sin_port;
			port = ntohs(port);
			if (port_is_okay(port) != 0) {
				return -1;
			}
			break;
		case AF_INET6:
			port = ((struct sockaddr_in6 *)dest_addr)->sin6_port;
			port = ntohs(port);
			if (port_is_okay(port) != 0) {
				return -1;
			}
			break;
		default:
			break;
		}
	}
	return next_sendto(sockfd, buf, len, flags, dest_addr, addrlen);
}