split acl and netsock code into separate files, in preparation for new netsock implem...
[asterisk/asterisk.git] / netsock.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Network socket handling
5  * 
6  * Copyright (C) 2004-2005, Digium, Inc.
7  *
8  * Mark Spencer <markster@digium.com>
9  * Kevin P. Fleming <kpfleming@digium.com>
10  *
11  * This program is free software, distributed under the terms of
12  * the GNU General Public License
13  */
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/time.h>
19 #include <signal.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
24 #include <sys/socket.h>
25 #include <netdb.h>
26 #include <net/if.h>
27 #include <netinet/in_systm.h>
28 #include <netinet/ip.h>
29 #include <sys/ioctl.h>
30
31 #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
32 #include <fcntl.h>
33 #include <net/route.h>
34 #endif
35
36 #if defined (SOLARIS)
37 #include <sys/sockio.h>
38 #endif
39
40 #include "asterisk.h"
41
42 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
43
44 #include "asterisk/netsock.h"
45 #include "asterisk/logger.h"
46 #include "asterisk/channel.h"
47 #include "asterisk/options.h"
48 #include "asterisk/utils.h"
49 #include "asterisk/lock.h"
50 #include "asterisk/srv.h"
51
52 struct ast_netsock {
53         ASTOBJ_COMPONENTS(struct ast_netsock);
54         struct sockaddr_in bindaddr;
55         int sockfd;
56         int *ioref;
57         struct io_context *ioc;
58         void *data;
59 };
60
61 struct ast_netsock_list {
62         ASTOBJ_CONTAINER_COMPONENTS(struct ast_netsock);
63         struct io_context *ioc;
64 };
65
66 static void ast_netsock_destroy(struct ast_netsock *netsock)
67 {
68         ast_io_remove(netsock->ioc, netsock->ioref);
69         close(netsock->sockfd);
70         free(netsock);
71 }
72
73 struct ast_netsock_list *ast_netsock_list_alloc(void)
74 {
75         struct ast_netsock_list *res;
76
77         res = calloc(1, sizeof(*res));
78
79         return res;
80 }
81
82 int ast_netsock_init(struct ast_netsock_list *list)
83 {
84         memset(list, 0, sizeof(*list));
85         ASTOBJ_CONTAINER_INIT(list);
86
87         return 0;
88 }
89
90 int ast_netsock_release(struct ast_netsock_list *list)
91 {
92         ASTOBJ_CONTAINER_DESTROYALL(list, ast_netsock_destroy);
93         ASTOBJ_CONTAINER_DESTROY(list);
94
95         return 0;
96 }
97
98 struct ast_netsock *ast_netsock_find(struct ast_netsock_list *list,
99                                      struct sockaddr_in *sa)
100 {
101         struct ast_netsock *sock = NULL;
102
103         ASTOBJ_CONTAINER_TRAVERSE(list, !sock, {
104                 ASTOBJ_RDLOCK(iterator);
105                 if (!inaddrcmp(&iterator->bindaddr, sa))
106                         sock = iterator;
107                 ASTOBJ_UNLOCK(iterator);
108         });
109
110         return sock;
111 }
112
113 struct ast_netsock *ast_netsock_bindaddr(struct ast_netsock_list *list, struct io_context *ioc, struct sockaddr_in *bindaddr, int tos, ast_io_cb callback, void *data)
114 {
115         int netsocket = -1;
116         int *ioref;
117         char iabuf[INET_ADDRSTRLEN];
118         
119         struct ast_netsock *ns;
120         
121         /* Make a UDP socket */
122         netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
123         
124         if (netsocket < 0) {
125                 ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
126                 return NULL;
127         }
128         if (bind(netsocket,(struct sockaddr *)bindaddr, sizeof(struct sockaddr_in))) {
129                 ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), bindaddr->sin_addr), ntohs(bindaddr->sin_port), strerror(errno));
130                 close(netsocket);
131                 return NULL;
132         }
133         if (option_verbose > 1)
134                 ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos);
135
136         if (setsockopt(netsocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) 
137                 ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
138
139         ns = malloc(sizeof(struct ast_netsock));
140         if (ns) {
141                 /* Establish I/O callback for socket read */
142                 ioref = ast_io_add(ioc, netsocket, callback, AST_IO_IN, ns);
143                 if (!ioref) {
144                         ast_log(LOG_WARNING, "Out of memory!\n");
145                         close(netsocket);
146                         free(ns);
147                         return NULL;
148                 }       
149                 ASTOBJ_INIT(ns);
150                 ns->ioref = ioref;
151                 ns->ioc = ioc;
152                 ns->sockfd = netsocket;
153                 ns->data = data;
154                 memcpy(&ns->bindaddr, bindaddr, sizeof(ns->bindaddr));
155                 ASTOBJ_CONTAINER_LINK(list, ns);
156         } else {
157                 ast_log(LOG_WARNING, "Out of memory!\n");
158                 close(netsocket);
159         }
160
161         return ns;
162 }
163
164 struct ast_netsock *ast_netsock_bind(struct ast_netsock_list *list, struct io_context *ioc, const char *bindinfo, int defaultport, int tos, ast_io_cb callback, void *data)
165 {
166         struct sockaddr_in sin;
167         char *tmp;
168         char *host;
169         char *port;
170         int portno;
171
172         memset(&sin, 0, sizeof(sin));
173         sin.sin_family = AF_INET;
174         sin.sin_port = htons(defaultport);
175         tmp = ast_strdupa(bindinfo);
176         if (!tmp) {
177                 ast_log(LOG_WARNING, "Out of memory!\n");
178                 return NULL;
179         }
180
181         host = strsep(&tmp, ":");
182         port = tmp;
183
184         if (port && ((portno = atoi(port)) > 0))
185                 sin.sin_port = htons(portno);
186
187         inet_aton(host, &sin.sin_addr);
188
189         return ast_netsock_bindaddr(list, ioc, &sin, tos, callback, data);
190 }
191
192 int ast_netsock_sockfd(const struct ast_netsock *ns)
193 {
194         return ns ? ns-> sockfd : -1;
195 }
196
197 const struct sockaddr_in *ast_netsock_boundaddr(const struct ast_netsock *ns)
198 {
199         return &(ns->bindaddr);
200 }
201
202 void *ast_netsock_data(const struct ast_netsock *ns)
203 {
204         return ns->data;
205 }