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