Do not accept new connections if the connection buffer is full.
[deb_shairplay.git] / src / lib / netutils.c
1 /**
2 * Copyright (C) 2011-2012 Juho Vähä-Herttua
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 */
14
15 #include <stdlib.h>
16 #include <string.h>
17 #include <assert.h>
18
19 #include "compat.h"
20
21 int
22 netutils_init()
23 {
24 #ifdef WIN32
25 WORD wVersionRequested;
26 WSADATA wsaData;
27 int ret;
28
29 wVersionRequested = MAKEWORD(2, 2);
30 ret = WSAStartup(wVersionRequested, &wsaData);
31 if (ret) {
32 return -1;
33 }
34
35 if (LOBYTE(wsaData.wVersion) != 2 ||
36 HIBYTE(wsaData.wVersion) != 2) {
37 /* Version mismatch, requested version not found */
38 return -1;
39 }
40 #endif
41 return 0;
42 }
43
44 void
45 netutils_cleanup()
46 {
47 #ifdef WIN32
48 WSACleanup();
49 #endif
50 }
51
52 int
53 netutils_init_socket(unsigned short *port, int use_ipv6, int use_udp)
54 {
55 int family = use_ipv6 ? AF_INET6 : AF_INET;
56 int type = use_udp ? SOCK_DGRAM : SOCK_STREAM;
57 int proto = use_udp ? IPPROTO_UDP : IPPROTO_TCP;
58
59 struct sockaddr_storage saddr;
60 socklen_t socklen;
61 int server_fd;
62 int ret;
63
64 assert(port);
65
66 server_fd = socket(family, type, proto);
67 if (server_fd == -1) {
68 goto cleanup;
69 }
70
71 memset(&saddr, 0, sizeof(saddr));
72 if (use_ipv6) {
73 struct sockaddr_in6 *sin6ptr = (struct sockaddr_in6 *)&saddr;
74
75 /* Initialize sockaddr for bind */
76 sin6ptr->sin6_family = family;
77 sin6ptr->sin6_addr = in6addr_any;
78 sin6ptr->sin6_port = htons(*port);
79
80 socklen = sizeof(*sin6ptr);
81 ret = bind(server_fd, (struct sockaddr *)sin6ptr, socklen);
82 if (ret == -1) {
83 goto cleanup;
84 }
85
86 ret = getsockname(server_fd, (struct sockaddr *)sin6ptr, &socklen);
87 if (ret == -1) {
88 goto cleanup;
89 }
90 *port = ntohs(sin6ptr->sin6_port);
91 } else {
92 struct sockaddr_in *sinptr = (struct sockaddr_in *)&saddr;
93
94 /* Initialize sockaddr for bind */
95 sinptr->sin_family = family;
96 sinptr->sin_addr.s_addr = INADDR_ANY;
97 sinptr->sin_port = htons(*port);
98
99 socklen = sizeof(*sinptr);
100 ret = bind(server_fd, (struct sockaddr *)sinptr, socklen);
101 if (ret == -1) {
102 goto cleanup;
103 }
104
105 ret = getsockname(server_fd, (struct sockaddr *)sinptr, &socklen);
106 if (ret == -1) {
107 goto cleanup;
108 }
109 *port = ntohs(sinptr->sin_port);
110 }
111 return server_fd;
112
113 cleanup:
114 ret = SOCKET_GET_ERROR();
115 if (server_fd != -1) {
116 closesocket(server_fd);
117 }
118 SOCKET_SET_ERROR(ret);
119 return -1;
120 }
121
122 unsigned char *
123 netutils_get_address(void *sockaddr, int *length)
124 {
125 unsigned char ipv4_prefix[] = { 0,0,0,0,0,0,0,0,0,0,255,255 };
126 struct sockaddr *address = sockaddr;
127
128 assert(address);
129 assert(length);
130
131 if (address->sa_family == AF_INET) {
132 struct sockaddr_in *sin;
133
134 sin = (struct sockaddr_in *)address;
135 *length = sizeof(sin->sin_addr.s_addr);
136 return (unsigned char *)&sin->sin_addr.s_addr;
137 } else if (address->sa_family == AF_INET6) {
138 struct sockaddr_in6 *sin6;
139
140 sin6 = (struct sockaddr_in6 *)address;
141 if (!memcmp(sin6->sin6_addr.s6_addr, ipv4_prefix, 12)) {
142 /* Actually an embedded IPv4 address */
143 *length = sizeof(sin6->sin6_addr.s6_addr)-12;
144 return (sin6->sin6_addr.s6_addr+12);
145 }
146 *length = sizeof(sin6->sin6_addr.s6_addr);
147 return sin6->sin6_addr.s6_addr;
148 }
149
150 *length = 0;
151 return NULL;
152 }
153