1109998Smarkm#include "tunala.h"
2109998Smarkm
3109998Smarkm#ifndef NO_IP
4109998Smarkm
5296465Sdelphij# define IP_LISTENER_BACKLOG 511/* So if it gets masked by 256 or some other
6296465Sdelphij                                 * such value it'll still be respectable */
7109998Smarkm
8109998Smarkm/* Any IP-related initialisations. For now, this means blocking SIGPIPE */
9109998Smarkmint ip_initialise(void)
10109998Smarkm{
11296465Sdelphij    struct sigaction sa;
12109998Smarkm
13296465Sdelphij    sa.sa_handler = SIG_IGN;
14296465Sdelphij    sa.sa_flags = 0;
15296465Sdelphij    sigemptyset(&sa.sa_mask);
16296465Sdelphij    if (sigaction(SIGPIPE, &sa, NULL) != 0)
17296465Sdelphij        return 0;
18296465Sdelphij    return 1;
19109998Smarkm}
20109998Smarkm
21109998Smarkmint ip_create_listener_split(const char *ip, unsigned short port)
22109998Smarkm{
23296465Sdelphij    struct sockaddr_in in_addr;
24296465Sdelphij    int fd = -1;
25296465Sdelphij    int reuseVal = 1;
26109998Smarkm
27296465Sdelphij    /* Create the socket */
28296465Sdelphij    if ((fd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
29296465Sdelphij        goto err;
30296465Sdelphij    /* Set the SO_REUSEADDR flag - servers act weird without it */
31296465Sdelphij    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)(&reuseVal),
32296465Sdelphij                   sizeof(reuseVal)) != 0)
33296465Sdelphij        goto err;
34296465Sdelphij    /* Prepare the listen address stuff */
35296465Sdelphij    in_addr.sin_family = AF_INET;
36296465Sdelphij    memcpy(&in_addr.sin_addr.s_addr, ip, 4);
37296465Sdelphij    in_addr.sin_port = htons(port);
38296465Sdelphij    /* Bind to the required port/address/interface */
39296465Sdelphij    if (bind(fd, (struct sockaddr *)&in_addr, sizeof(struct sockaddr_in)) !=
40296465Sdelphij        0)
41296465Sdelphij        goto err;
42296465Sdelphij    /* Start "listening" */
43296465Sdelphij    if (listen(fd, IP_LISTENER_BACKLOG) != 0)
44296465Sdelphij        goto err;
45296465Sdelphij    return fd;
46296465Sdelphij err:
47296465Sdelphij    if (fd != -1)
48296465Sdelphij        close(fd);
49296465Sdelphij    return -1;
50109998Smarkm}
51109998Smarkm
52109998Smarkmint ip_create_connection_split(const char *ip, unsigned short port)
53109998Smarkm{
54296465Sdelphij    struct sockaddr_in in_addr;
55296465Sdelphij    int flags, fd = -1;
56109998Smarkm
57296465Sdelphij    /* Create the socket */
58296465Sdelphij    if ((fd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
59296465Sdelphij        goto err;
60296465Sdelphij    /* Make it non-blocking */
61296465Sdelphij    if (((flags = fcntl(fd, F_GETFL, 0)) < 0) ||
62296465Sdelphij        (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0))
63296465Sdelphij        goto err;
64296465Sdelphij    /* Prepare the connection address stuff */
65296465Sdelphij    in_addr.sin_family = AF_INET;
66296465Sdelphij    memcpy(&in_addr.sin_addr.s_addr, ip, 4);
67296465Sdelphij    in_addr.sin_port = htons(port);
68296465Sdelphij    /* Start a connect (non-blocking, in all likelihood) */
69296465Sdelphij    if ((connect(fd, (struct sockaddr *)&in_addr,
70296465Sdelphij                 sizeof(struct sockaddr_in)) != 0) && (errno != EINPROGRESS))
71296465Sdelphij        goto err;
72296465Sdelphij    return fd;
73296465Sdelphij err:
74296465Sdelphij    if (fd != -1)
75296465Sdelphij        close(fd);
76296465Sdelphij    return -1;
77109998Smarkm}
78109998Smarkm
79296465Sdelphijstatic char all_local_ip[] = { 0x00, 0x00, 0x00, 0x00 };
80109998Smarkm
81109998Smarkmint ip_parse_address(const char *address, const char **parsed_ip,
82296465Sdelphij                     unsigned short *parsed_port, int accept_all_ip)
83109998Smarkm{
84296465Sdelphij    char buf[256];
85296465Sdelphij    struct hostent *lookup;
86296465Sdelphij    unsigned long port;
87296465Sdelphij    const char *ptr = strstr(address, ":");
88296465Sdelphij    const char *ip = all_local_ip;
89109998Smarkm
90296465Sdelphij    if (!ptr) {
91296465Sdelphij        /*
92296465Sdelphij         * We assume we're listening on all local interfaces and have only
93296465Sdelphij         * specified a port.
94296465Sdelphij         */
95296465Sdelphij        if (!accept_all_ip)
96296465Sdelphij            return 0;
97296465Sdelphij        ptr = address;
98296465Sdelphij        goto determine_port;
99296465Sdelphij    }
100296465Sdelphij    if ((ptr - address) > 255)
101296465Sdelphij        return 0;
102296465Sdelphij    memset(buf, 0, 256);
103296465Sdelphij    memcpy(buf, address, ptr - address);
104296465Sdelphij    ptr++;
105296465Sdelphij    if ((lookup = gethostbyname(buf)) == NULL) {
106296465Sdelphij        /*
107296465Sdelphij         * Spit a message to differentiate between lookup failures and bad
108296465Sdelphij         * strings.
109296465Sdelphij         */
110296465Sdelphij        fprintf(stderr, "hostname lookup for '%s' failed\n", buf);
111296465Sdelphij        return 0;
112296465Sdelphij    }
113296465Sdelphij    ip = lookup->h_addr_list[0];
114296465Sdelphij determine_port:
115296465Sdelphij    if (strlen(ptr) < 1)
116296465Sdelphij        return 0;
117296465Sdelphij    if (!int_strtoul(ptr, &port) || (port > 65535))
118296465Sdelphij        return 0;
119296465Sdelphij    *parsed_ip = ip;
120296465Sdelphij    *parsed_port = (unsigned short)port;
121296465Sdelphij    return 1;
122109998Smarkm}
123109998Smarkm
124109998Smarkmint ip_create_listener(const char *address)
125109998Smarkm{
126296465Sdelphij    const char *ip;
127296465Sdelphij    unsigned short port;
128109998Smarkm
129296465Sdelphij    if (!ip_parse_address(address, &ip, &port, 1))
130296465Sdelphij        return -1;
131296465Sdelphij    return ip_create_listener_split(ip, port);
132109998Smarkm}
133109998Smarkm
134109998Smarkmint ip_create_connection(const char *address)
135109998Smarkm{
136296465Sdelphij    const char *ip;
137296465Sdelphij    unsigned short port;
138109998Smarkm
139296465Sdelphij    if (!ip_parse_address(address, &ip, &port, 0))
140296465Sdelphij        return -1;
141296465Sdelphij    return ip_create_connection_split(ip, port);
142109998Smarkm}
143109998Smarkm
144109998Smarkmint ip_accept_connection(int listen_fd)
145109998Smarkm{
146296465Sdelphij    return accept(listen_fd, NULL, NULL);
147109998Smarkm}
148109998Smarkm
149296465Sdelphij#endif                          /* !defined(NO_IP) */
150