/src/opensips/socket_info.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2001-2003 FhG Fokus |
3 | | * |
4 | | * This file is part of opensips, a free SIP server. |
5 | | * |
6 | | * opensips is free software; you can redistribute it and/or modify |
7 | | * it under the terms of the GNU General Public License as published by |
8 | | * the Free Software Foundation; either version 2 of the License, or |
9 | | * (at your option) any later version |
10 | | * |
11 | | * opensips is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public License |
17 | | * along with this program; if not, write to the Free Software |
18 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | | * |
20 | | * |
21 | | * This file contains code that initializes and handles ser listen addresses |
22 | | * lists (struct socket_info). It is used mainly on startup. |
23 | | * |
24 | | * History: |
25 | | * -------- |
26 | | * 2003-10-22 created by andrei |
27 | | * 2004-10-10 added grep_sock_info (andrei) |
28 | | * 2004-11-08 added find_si (andrei) |
29 | | * 2007-01-11 auto_aliases option added (bogdan) |
30 | | */ |
31 | | |
32 | | /*! |
33 | | * \file |
34 | | * \brief Find & manage listen addresses |
35 | | */ |
36 | | |
37 | | |
38 | | #include <string.h> |
39 | | #include <errno.h> |
40 | | #include <unistd.h> |
41 | | #include <sys/types.h> |
42 | | #include <sys/socket.h> |
43 | | #include <sys/utsname.h> |
44 | | #include <stdio.h> |
45 | | |
46 | | #include <sys/ioctl.h> |
47 | | #include <net/if.h> |
48 | | #ifdef HAVE_SYS_SOCKIO_H |
49 | | #include <sys/sockio.h> |
50 | | #endif |
51 | | |
52 | | #include "str.h" |
53 | | #include "globals.h" |
54 | | #include "socket_info.h" |
55 | | #include "dprint.h" |
56 | | #include "mem/mem.h" |
57 | | #include "ut.h" |
58 | | #include "pt_scaling.h" |
59 | | #include "resolve.h" |
60 | | #include "name_alias.h" |
61 | | #include "net/trans.h" |
62 | | |
63 | | #ifdef __OS_linux |
64 | | #include <features.h> /* for GLIBC version testing */ |
65 | | #if defined(__GLIBC_PREREQ) |
66 | | #if __GLIBC_PREREQ(2, 4) |
67 | | #include <ifaddrs.h> |
68 | | #define HAVE_IFADDRS |
69 | | #endif |
70 | | #endif |
71 | | #endif |
72 | | |
73 | 0 | #define MAX_PROC_BUFFER 256 |
74 | | |
75 | | /* list manip. functions (internal use only) */ |
76 | | |
77 | | |
78 | | /* append */ |
79 | | #define sock_listadd(head, el) \ |
80 | 0 | do{\ |
81 | 0 | if (*(head)==0) *(head)=(el); \ |
82 | 0 | else{ \ |
83 | 0 | for((el)->next=*(head); (el)->next->next;\ |
84 | 0 | (el)->next=(el)->next->next); \ |
85 | 0 | (el)->next->next=(el); \ |
86 | 0 | (el)->prev=(el)->next; \ |
87 | 0 | (el)->next=0; \ |
88 | 0 | }\ |
89 | 0 | }while(0) |
90 | | |
91 | | |
92 | | /* insert after "after" */ |
93 | | #define sock_listins(el, after) \ |
94 | | do{ \ |
95 | | if ((after)){\ |
96 | | (el)->next=(after)->next; \ |
97 | | if ((after)->next) (after)->next->prev=(el); \ |
98 | | (after)->next=(el); \ |
99 | | (el)->prev=(after); \ |
100 | | }else{ /* after==0 = list head */ \ |
101 | | (after)=(el); \ |
102 | | (el)->next=(el)->prev=0; \ |
103 | | }\ |
104 | | }while(0) |
105 | | |
106 | | |
107 | | #define sock_listrm(head, el) \ |
108 | 0 | do {\ |
109 | 0 | if (*(head)==(el)) *(head)=(el)->next; \ |
110 | 0 | if ((el)->next) (el)->next->prev=(el)->prev; \ |
111 | 0 | if ((el)->prev) (el)->prev->next=(el)->next; \ |
112 | 0 | }while(0) |
113 | | |
114 | | |
115 | | /* another helper function, it just creates a socket_info struct */ |
116 | | struct socket_info_full* new_sock_info( struct socket_id *sid) |
117 | 0 | { |
118 | 0 | struct socket_info_full *sif; |
119 | 0 | struct socket_info *si; |
120 | |
|
121 | 0 | sif=(struct socket_info_full*) pkg_malloc(sizeof(*sif)); |
122 | 0 | if (sif==NULL) goto error; |
123 | 0 | memset(sif, 0, sizeof(*sif)); |
124 | 0 | si = &sif->socket_info; |
125 | 0 | si->socket=-1; |
126 | 0 | si->last_real_ports = &sif->last_real_ports; |
127 | |
|
128 | 0 | if (sid->name) { |
129 | 0 | si->name.len=strlen(sid->name); |
130 | 0 | si->name.s=(char*)pkg_malloc(si->name.len+1); /* include \0 */ |
131 | 0 | if (si->name.s==0) goto error; |
132 | 0 | memcpy(si->name.s, sid->name, si->name.len+1); |
133 | 0 | } |
134 | | |
135 | | /* set port & proto */ |
136 | 0 | si->port_no=sid->port; |
137 | 0 | si->proto=sid->proto; |
138 | 0 | si->flags=sid->flags; |
139 | | |
140 | | /* advertised socket information */ |
141 | | /* Make sure the adv_sock_string is initialized, because if there is |
142 | | * no adv_sock_name, no other code will initialize it! |
143 | | */ |
144 | 0 | si->adv_sock_str.s=NULL; |
145 | 0 | si->adv_sock_str.len=0; |
146 | 0 | si->adv_port = 0; /* Here to help grep_sock_info along. */ |
147 | 0 | if(sid->adv_name) { |
148 | 0 | si->adv_name_str.len=strlen(sid->adv_name); |
149 | 0 | si->adv_name_str.s=(char *)pkg_malloc(si->adv_name_str.len+1); |
150 | 0 | if (si->adv_name_str.s==0) goto error; |
151 | 0 | memcpy(si->adv_name_str.s, sid->adv_name, si->adv_name_str.len+1); |
152 | 0 | if (!sid->adv_port) sid->adv_port=si->port_no ; |
153 | 0 | si->adv_port_str.s=pkg_malloc(10); |
154 | 0 | if (si->adv_port_str.s==0) goto error; |
155 | 0 | si->adv_port_str.len=snprintf(si->adv_port_str.s, 10, "%hu", |
156 | 0 | (unsigned short)sid->adv_port); |
157 | 0 | si->adv_port = sid->adv_port; |
158 | 0 | } |
159 | | |
160 | | /* store the tag info too */ |
161 | 0 | if (sid->tag) { |
162 | 0 | si->tag.len = strlen(sid->tag); |
163 | 0 | si->tag.s=(char*)pkg_malloc(si->tag.len+1); /* include \0 */ |
164 | 0 | if (si->tag.s==0) goto error; |
165 | 0 | memcpy(si->tag.s, sid->tag, si->tag.len+1); |
166 | 0 | } |
167 | | |
168 | 0 | if (si->proto!=PROTO_UDP && si->proto!=PROTO_SCTP && |
169 | 0 | si->proto!=PROTO_HEP_UDP) { |
170 | 0 | if (sid->workers) |
171 | 0 | LM_WARN("number of workers per non UDP-based <%.*s> listener not " |
172 | 0 | "supported -> ignoring...\n", si->name.len, si->name.s); |
173 | 0 | if (sid->auto_scaling_profile) |
174 | 0 | LM_WARN("auto-scaling for non UDP-based <%.*s> listener not " |
175 | 0 | "supported -> ignoring...\n", si->name.len, si->name.s); |
176 | 0 | } else { |
177 | 0 | if (sid->workers) |
178 | 0 | si->workers = sid->workers; |
179 | 0 | si->tos = sid->tos; |
180 | 0 | if (sid->auto_scaling_profile) { |
181 | 0 | si->s_profile = get_scaling_profile(sid->auto_scaling_profile); |
182 | 0 | if (si->s_profile==NULL) { |
183 | 0 | LM_WARN("scaling profile <%s> in listener <%.*s> not defined " |
184 | 0 | "-> ignoring it...\n", sid->auto_scaling_profile, |
185 | 0 | si->name.len, si->name.s); |
186 | 0 | } else { |
187 | 0 | auto_scaling_enabled = 1; |
188 | 0 | } |
189 | 0 | } else if (udp_auto_scaling_profile) { |
190 | 0 | si->s_profile = get_scaling_profile(udp_auto_scaling_profile); |
191 | 0 | if (si->s_profile==NULL) { |
192 | 0 | LM_WARN("scaling profile <%s> in udp_workers not defined " |
193 | 0 | "-> ignoring it...\n", udp_auto_scaling_profile); |
194 | 0 | } else { |
195 | 0 | auto_scaling_enabled = 1; |
196 | 0 | } |
197 | 0 | } |
198 | 0 | } |
199 | 0 | return sif; |
200 | 0 | error: |
201 | 0 | LM_ERR("pkg memory allocation error\n"); |
202 | 0 | if (sif) pkg_free(sif); |
203 | 0 | return 0; |
204 | 0 | } |
205 | | |
206 | | |
207 | | |
208 | | /* delete a socket_info struct */ |
209 | | void free_sock_info(struct socket_info_full* sif) |
210 | 0 | { |
211 | 0 | if(sif){ |
212 | 0 | struct socket_info *si = &sif->socket_info; |
213 | 0 | if(si->name.s) pkg_free(si->name.s); |
214 | 0 | if(si->tag.s) pkg_free(si->tag.s); |
215 | 0 | if(si->sock_str.s) pkg_free(si->sock_str.s); |
216 | 0 | if(si->address_str.s) pkg_free(si->address_str.s); |
217 | 0 | if(si->port_no_str.s) pkg_free(si->port_no_str.s); |
218 | 0 | if(si->adv_name_str.s) pkg_free(si->adv_name_str.s); |
219 | 0 | if(si->adv_port_str.s) pkg_free(si->adv_port_str.s); |
220 | 0 | if(si->adv_sock_str.s) pkg_free(si->adv_sock_str.s); |
221 | 0 | if(si->tag_sock_str.s) pkg_free(si->tag_sock_str.s); |
222 | 0 | } |
223 | 0 | } |
224 | | |
225 | | |
226 | | /* checks if the proto: host:port is one of the address we listen on |
227 | | * and returns the corresponding socket_info structure. |
228 | | * if port==0, the port number is ignored |
229 | | * if proto==0 (PROTO_NONE) the protocol is ignored |
230 | | * returns 0 if not found |
231 | | * WARNING: uses str2ip6 so it will overwrite any previous |
232 | | * unsaved result of this function (static buffer) |
233 | | */ |
234 | | const struct socket_info* grep_sock_info_ext(str* host, unsigned short port, |
235 | | unsigned short proto, int check_tags) |
236 | 0 | { |
237 | 0 | char* hname; |
238 | 0 | int h_len; |
239 | 0 | const struct socket_info* si = NULL; |
240 | 0 | struct socket_info_full* sif; |
241 | 0 | struct socket_info_full ** list; |
242 | 0 | unsigned short c_proto; |
243 | 0 | struct ip_addr* ip6; |
244 | |
|
245 | 0 | h_len=host->len; |
246 | 0 | hname=host->s; |
247 | |
|
248 | 0 | if ((h_len>2)&&((*hname)=='[')&&(hname[h_len-1]==']')){ |
249 | | /* ipv6 reference, skip [] */ |
250 | 0 | hname++; |
251 | 0 | h_len-=2; |
252 | 0 | } |
253 | |
|
254 | 0 | c_proto=proto?proto:PROTO_UDP; |
255 | 0 | do{ |
256 | | /* "proto" is all the time valid here */ |
257 | 0 | list=get_sock_info_list(c_proto); |
258 | |
|
259 | 0 | if (list==0){ |
260 | 0 | LM_WARN("unknown proto %d\n", c_proto); |
261 | 0 | goto not_found; /* false */ |
262 | 0 | } |
263 | 0 | for (sif=*list; sif; sif=sif->next){ |
264 | 0 | si = &sif->socket_info; |
265 | 0 | LM_DBG("checking if host==us: %d==%d && " |
266 | 0 | " [%.*s] == [%.*s]\n", |
267 | 0 | h_len, |
268 | 0 | si->name.len, |
269 | 0 | h_len, hname, |
270 | 0 | si->name.len, si->name.s |
271 | 0 | ); |
272 | |
|
273 | 0 | if (check_tags && port==0 && si->tag.s && h_len==si->tag.len && |
274 | 0 | strncasecmp(hname, si->tag.s, si->tag.len)==0 ) |
275 | 0 | goto found; |
276 | | |
277 | 0 | if (port) { |
278 | 0 | LM_DBG("checking if port %d matches port %d\n", |
279 | 0 | si->port_no, port); |
280 | 0 | if ((si->port_no != 0 || protos[c_proto].default_port != port) && |
281 | 0 | (si->port_no == 0 || si->port_no != port) && |
282 | 0 | si->adv_port!=port) { |
283 | 0 | continue; |
284 | 0 | } |
285 | 0 | } |
286 | 0 | if ( (h_len==si->name.len) && |
287 | 0 | (strncasecmp(hname, si->name.s, |
288 | 0 | si->name.len)==0) /*slower*/) |
289 | | /* comp. must be case insensitive, host names |
290 | | * can be written in mixed case, it will also match |
291 | | * ipv6 addresses if we are lucky*/ |
292 | 0 | goto found; |
293 | | /* Check if the adv. name of this socket matches */ |
294 | 0 | if ( (h_len==si->adv_name_str.len) && |
295 | 0 | (strncasecmp(hname, si->adv_name_str.s, |
296 | 0 | si->adv_name_str.len)==0) /*slower*/) |
297 | | /* comp. must be case insensitive, host names |
298 | | * can be in mixed case, it will also match |
299 | | * ipv6 addresses if we are lucky*/ |
300 | 0 | goto found; |
301 | | /* if no advertised is specified on the interface, we should check |
302 | | * if it is the global address */ |
303 | 0 | if (!si->adv_name_str.len && default_global_address->s && |
304 | 0 | h_len == default_global_address->len && |
305 | 0 | (strncasecmp(hname, default_global_address->s, |
306 | 0 | default_global_address->len)==0) /*slower*/) |
307 | | /* this might match sockets that are not supposed to |
308 | | * match, when using multiple listeners for the same |
309 | | * protocol; but in that case the default_global_address |
310 | | * concept is broken, since there is no way to choose |
311 | | * the right socket */ |
312 | 0 | goto found; |
313 | | /* check if host == ip address */ |
314 | | /* ipv6 case is uglier, host can be [3ffe::1] */ |
315 | 0 | ip6=str2ip6(host); |
316 | 0 | if (ip6){ |
317 | 0 | if (ip_addr_cmp(ip6, &si->address)) |
318 | 0 | goto found; /* match */ |
319 | 0 | else |
320 | 0 | if (si->adv_name_str.len && ip_addr_cmp(ip6,&si->adv_address)) |
321 | 0 | goto found; |
322 | 0 | else |
323 | 0 | continue; /* no match, but this is an ipv6 address |
324 | | so no point in trying ipv4 */ |
325 | 0 | } |
326 | | /* ipv4 */ |
327 | 0 | if ( (!(si->flags&SI_IS_IP)) && |
328 | 0 | (h_len==si->address_str.len) && |
329 | 0 | (memcmp(hname, si->address_str.s, |
330 | 0 | si->address_str.len)==0) |
331 | 0 | ) |
332 | 0 | goto found; |
333 | 0 | } |
334 | 0 | }while( (proto==0) && (c_proto=next_proto(c_proto)) ); |
335 | 0 | not_found: |
336 | 0 | return 0; |
337 | 0 | found: |
338 | 0 | return si; |
339 | 0 | } |
340 | | |
341 | | |
342 | | |
343 | | /* checks if the proto: ip:port is one of the address we listen on |
344 | | * and returns the corresponding socket_info structure. |
345 | | * (same as grep_socket_info, but use ip addr instead) |
346 | | * if port==0, the port number is ignored |
347 | | * if proto==0 (PROTO_NONE) the protocol is ignored |
348 | | * returns 0 if not found |
349 | | * WARNING: uses str2ip6 so it will overwrite any previous |
350 | | * unsaved result of this function (static buffer) |
351 | | */ |
352 | | const struct socket_info* find_si(const struct ip_addr* ip, unsigned short port, |
353 | | unsigned short proto) |
354 | 0 | { |
355 | 0 | const struct socket_info* si = NULL; |
356 | 0 | struct socket_info_full* sif; |
357 | 0 | struct socket_info_full** list; |
358 | 0 | unsigned short c_proto; |
359 | |
|
360 | 0 | c_proto=proto?proto:PROTO_UDP; |
361 | 0 | do{ |
362 | | /* get the proper sock_list */ |
363 | 0 | list=get_sock_info_list(c_proto); |
364 | |
|
365 | 0 | if (list==0){ |
366 | 0 | LM_WARN("unknown proto %d\n", c_proto); |
367 | 0 | goto not_found; /* false */ |
368 | 0 | } |
369 | 0 | for (sif=*list; sif; sif=sif->next){ |
370 | 0 | si = &sif->socket_info; |
371 | 0 | if (port) { |
372 | 0 | if (si->port_no!=port) { |
373 | 0 | continue; |
374 | 0 | } |
375 | 0 | } |
376 | 0 | if (ip_addr_cmp(ip, &si->address) || ip_addr_cmp(ip, &si->adv_address)) |
377 | 0 | goto found; |
378 | 0 | } |
379 | 0 | }while( (proto==0) && (c_proto=next_proto(c_proto)) ); |
380 | 0 | not_found: |
381 | 0 | return 0; |
382 | 0 | found: |
383 | 0 | return si; |
384 | 0 | } |
385 | | |
386 | | |
387 | | /* parses the specified `spec` and returns an associated |
388 | | * socket_info*, if it could be found */ |
389 | | const struct socket_info* parse_sock_info(str *addr) |
390 | 0 | { |
391 | 0 | int port, proto; |
392 | 0 | str host; |
393 | |
|
394 | 0 | if (!addr || !addr->s) |
395 | 0 | return NULL; |
396 | | |
397 | 0 | if (parse_phostport(addr->s, addr->len, &host.s, &host.len, |
398 | 0 | &port, &proto) != 0) { |
399 | 0 | return NULL; |
400 | 0 | } |
401 | | |
402 | 0 | return grep_internal_sock_info(&host, (unsigned short) port, |
403 | 0 | (unsigned short) proto); |
404 | 0 | } |
405 | | |
406 | | |
407 | | /* adds a new sock_info structure to the corresponding list |
408 | | * return 0 on success, -1 on error */ |
409 | | int new_sock2list(struct socket_id *sid, struct socket_info_full** list) |
410 | 0 | { |
411 | 0 | struct socket_info_full* si; |
412 | |
|
413 | 0 | si=new_sock_info(sid); |
414 | 0 | if (si==0){ |
415 | 0 | LM_ERR("new_sock_info failed\n"); |
416 | 0 | goto error; |
417 | 0 | } |
418 | 0 | sock_listadd(list, si); |
419 | 0 | return 0; |
420 | 0 | error: |
421 | 0 | return -1; |
422 | 0 | } |
423 | | |
424 | | void push_sock2list(struct socket_info_full *si) |
425 | 0 | { |
426 | 0 | sock_listadd(&protos[si->socket_info.proto].listeners, si); |
427 | 0 | } |
428 | | |
429 | | void pop_sock2list(struct socket_info_full *si) |
430 | 0 | { |
431 | 0 | sock_listrm(&protos[si->socket_info.proto].listeners, si); |
432 | 0 | } |
433 | | |
434 | | int update_default_socket_info(struct socket_info *si) |
435 | 0 | { |
436 | 0 | switch (si->address.af) { |
437 | 0 | case AF_INET: |
438 | 0 | if (protos[si->proto].sendipv4 && |
439 | 0 | (protos[si->proto].sendipv4->flags&SI_IS_LO) == 0) |
440 | 0 | return 0; |
441 | 0 | protos[si->proto].sendipv4 = si; |
442 | 0 | return 1; |
443 | 0 | case AF_INET6: |
444 | 0 | if (protos[si->proto].sendipv6 && |
445 | 0 | (protos[si->proto].sendipv6->flags&SI_IS_LO) == 0) |
446 | 0 | return 0; |
447 | 0 | protos[si->proto].sendipv6 = si; |
448 | 0 | return 1; |
449 | 0 | default: |
450 | | /* do nothing */ |
451 | 0 | return 0; |
452 | 0 | } |
453 | 0 | } |
454 | | |
455 | | void remove_default_socket_info(struct socket_info *si) |
456 | 0 | { |
457 | 0 | struct socket_info_full *sif; |
458 | 0 | switch (si->address.af) { |
459 | 0 | case AF_INET: |
460 | 0 | if (si != protos[si->proto].sendipv4) |
461 | 0 | return; |
462 | 0 | protos[si->proto].sendipv4 = NULL; |
463 | 0 | break; |
464 | 0 | case AF_INET6: |
465 | 0 | if (si != protos[si->proto].sendipv6) |
466 | 0 | return; |
467 | 0 | protos[si->proto].sendipv6 = NULL; |
468 | 0 | break; |
469 | 0 | default: |
470 | | /* do nothing */ |
471 | 0 | return; |
472 | 0 | } |
473 | 0 | for (sif = protos[si->proto].listeners; sif; sif = sif->next) |
474 | 0 | if (update_default_socket_info(&sif->socket_info)) |
475 | 0 | return; |
476 | 0 | } |
477 | | |
478 | | /* add all family type addresses of interface if_name to the socket_info array |
479 | | * WARNING: it only works with ipv6 addresses on FreeBSD |
480 | | * return: -1 on error, 0 on success |
481 | | */ |
482 | | static int expand_interface(const struct socket_info *si, struct socket_info_full** list) |
483 | 0 | { |
484 | 0 | int ret = -1; |
485 | 0 | struct ip_addr addr; |
486 | 0 | struct socket_id sid; |
487 | |
|
488 | 0 | sid.port = si->port_no; |
489 | 0 | sid.proto = si->proto; |
490 | 0 | sid.workers = si->workers; |
491 | 0 | sid.tos = si->tos; |
492 | 0 | sid.auto_scaling_profile = si->s_profile?si->s_profile->name:NULL; |
493 | 0 | sid.adv_port = si->adv_port; |
494 | 0 | sid.adv_name = si->adv_name_str.s; /* it is NULL terminated */ |
495 | 0 | sid.tag = si->tag.s; /* it is NULL terminated */ |
496 | 0 | #ifdef HAVE_IFADDRS |
497 | | /* use the getifaddrs interface to get all the interfaces */ |
498 | 0 | struct ifaddrs *addrs; |
499 | 0 | struct ifaddrs *it; |
500 | |
|
501 | 0 | if (getifaddrs(&addrs) != 0) { |
502 | 0 | LM_ERR("cannot get interfaces list: %s(%d)\n", strerror(errno), errno); |
503 | 0 | return -1; |
504 | 0 | } |
505 | | |
506 | 0 | for (it = addrs; it; it = it->ifa_next) { |
507 | 0 | if (!it->ifa_addr) |
508 | 0 | continue; |
509 | | |
510 | 0 | if (si->name.len == 0 || (strcmp(si->name.s, it->ifa_name) == 0)) { |
511 | 0 | if (it->ifa_addr->sa_family != AF_INET && |
512 | 0 | it->ifa_addr->sa_family != AF_INET6) |
513 | 0 | continue; |
514 | | /* |
515 | | * if it is ipv6, and there was no explicit interface specified, |
516 | | * make sure we don't add any "scoped" interface |
517 | | */ |
518 | 0 | if (it->ifa_addr->sa_family == AF_INET6 && |
519 | 0 | (((struct sockaddr_in6 *)(void *)it->ifa_addr)->sin6_scope_id != 0)) |
520 | | |
521 | 0 | continue; |
522 | 0 | sockaddr2ip_addr(&addr, it->ifa_addr); |
523 | 0 | if ((sid.name = ip_addr2a(&addr)) == 0) |
524 | 0 | goto end; |
525 | 0 | sid.flags = si->flags; |
526 | 0 | if (it->ifa_flags & IFF_LOOPBACK) |
527 | 0 | sid.flags |= SI_IS_LO; |
528 | 0 | if (new_sock2list(&sid, list) != 0) { |
529 | 0 | LM_ERR("clone_sock2list failed\n"); |
530 | 0 | goto end; |
531 | 0 | } |
532 | 0 | ret = 0; |
533 | 0 | } |
534 | 0 | } |
535 | 0 | end: |
536 | 0 | freeifaddrs(addrs); |
537 | 0 | return ret; |
538 | | #else |
539 | | struct ifconf ifc; |
540 | | struct ifreq ifr; |
541 | | struct ifreq ifrcopy; |
542 | | char* last; |
543 | | char* p; |
544 | | int size; |
545 | | int lastlen; |
546 | | int s; |
547 | | |
548 | | #ifdef HAVE_SOCKADDR_SA_LEN |
549 | | #ifndef MAX |
550 | | #define MAX(a,b) ( ((a)>(b))?(a):(b)) |
551 | | #endif |
552 | | #endif |
553 | | /* ipv4 or ipv6 only*/ |
554 | | s=socket(AF_INET, SOCK_DGRAM, 0); |
555 | | lastlen=0; |
556 | | ifc.ifc_req=0; |
557 | | for (size=100; ; size*=2){ |
558 | | ifc.ifc_len=size*sizeof(struct ifreq); |
559 | | ifc.ifc_req=(struct ifreq*) pkg_malloc(size*sizeof(struct ifreq)); |
560 | | if (ifc.ifc_req==0){ |
561 | | LM_ERR("memory allocation failure\n"); |
562 | | goto error; |
563 | | } |
564 | | if (ioctl(s, SIOCGIFCONF, &ifc)==-1){ |
565 | | if(errno==EBADF) goto error; /* invalid descriptor => no such ifs*/ |
566 | | LM_ERR("ioctl failed: %s\n", strerror(errno)); |
567 | | goto error; |
568 | | } |
569 | | if ((lastlen) && (ifc.ifc_len==lastlen)) break; /*success, |
570 | | len not changed*/ |
571 | | lastlen=ifc.ifc_len; |
572 | | /* try a bigger array*/ |
573 | | pkg_free(ifc.ifc_req); |
574 | | } |
575 | | |
576 | | last=(char*)ifc.ifc_req+ifc.ifc_len; |
577 | | for(p=(char*)ifc.ifc_req; p<last; |
578 | | p+= |
579 | | #ifdef __OS_linux |
580 | | sizeof(ifr) /* works on x86_64 too */ |
581 | | #else |
582 | | (sizeof(ifr.ifr_name)+ |
583 | | #ifdef HAVE_SOCKADDR_SA_LEN |
584 | | MAX(ifr.ifr_addr.sa_len, sizeof(struct sockaddr)) |
585 | | #else |
586 | | ( (ifr.ifr_addr.sa_family==AF_INET)? |
587 | | sizeof(struct sockaddr_in): |
588 | | ((ifr.ifr_addr.sa_family==AF_INET6)? |
589 | | sizeof(struct sockaddr_in6):sizeof(struct sockaddr)) ) |
590 | | #endif |
591 | | ) |
592 | | #endif |
593 | | ) |
594 | | { |
595 | | /* copy contents into ifr structure |
596 | | * warning: it might be longer (e.g. ipv6 address) */ |
597 | | memcpy(&ifr, p, sizeof(ifr)); |
598 | | if (ifr.ifr_addr.sa_family!=AF_INET){ |
599 | | /*printf("strange family %d skipping...\n", |
600 | | ifr->ifr_addr.sa_family);*/ |
601 | | continue; |
602 | | } |
603 | | |
604 | | /*get flags*/ |
605 | | ifrcopy=ifr; |
606 | | if (ioctl(s, SIOCGIFFLAGS, &ifrcopy)!=-1){ /* ignore errors */ |
607 | | /* ignore down ifs only if listening on all of them*/ |
608 | | if (si->name.len==0){ |
609 | | /* if if not up, skip it*/ |
610 | | if (!(ifrcopy.ifr_flags & IFF_UP)) continue; |
611 | | } |
612 | | } |
613 | | |
614 | | if (si->name.len == 0 || |
615 | | strncmp(si->name.s, ifr.ifr_name, sizeof(ifr.ifr_name))==0){ |
616 | | |
617 | | /*add address*/ |
618 | | sockaddr2ip_addr(&addr, |
619 | | (struct sockaddr*)(p+(long)&((struct ifreq*)0)->ifr_addr)); |
620 | | if ((sid.name=ip_addr2a(&addr))==0) goto error; |
621 | | sid.flags = si->flags; |
622 | | /* check if loopback */ |
623 | | if (ifrcopy.ifr_flags & IFF_LOOPBACK) |
624 | | sid.flags|=SI_IS_LO; |
625 | | /* add it to one of the lists */ |
626 | | if (new_sock2list(&sid, list) != 0) { |
627 | | LM_ERR("clone_sock2list failed\n"); |
628 | | goto error; |
629 | | } |
630 | | ret=0; |
631 | | } |
632 | | /* |
633 | | printf("%s:\n", ifr->ifr_name); |
634 | | printf(" "); |
635 | | print_sockaddr(&(ifr->ifr_addr)); |
636 | | printf(" "); |
637 | | ls_ifflags(ifr->ifr_name, family, options); |
638 | | printf("\n");*/ |
639 | | } |
640 | | pkg_free(ifc.ifc_req); /*clean up*/ |
641 | | close(s); |
642 | | return ret; |
643 | | error: |
644 | | if (ifc.ifc_req) pkg_free(ifc.ifc_req); |
645 | | if (s >= 0) |
646 | | close(s); |
647 | | return -1; |
648 | | #endif |
649 | 0 | } |
650 | | |
651 | | |
652 | 0 | #define STR_IMATCH(str, buf) ((str).len==strlen(buf) && strncasecmp(buf, (str).s, (str).len)==0) |
653 | 0 | #define ACCEPT_SUBDOMAIN_ALIAS(flags) flags & SI_ACCEPT_SUBDOMAIN_ALIAS |
654 | | |
655 | | int is_localhost(struct socket_info *si) |
656 | 0 | { |
657 | 0 | return (STR_IMATCH(si->name, "localhost") || |
658 | 0 | STR_IMATCH(si->name, "127.0.0.1") || |
659 | 0 | STR_IMATCH(si->name, "0:0:0:0:0:0:0:1") || STR_IMATCH(si->name, "::1")); |
660 | 0 | } |
661 | | |
662 | | int fix_socket(struct socket_info_full *sif, int add_aliases) |
663 | 0 | { |
664 | 0 | char** h; |
665 | 0 | char* tmp; |
666 | 0 | int len; |
667 | 0 | struct hostent* he; |
668 | 0 | struct socket_info *si = &sif->socket_info; |
669 | | |
670 | | /* fix the number of processes per interface */ |
671 | 0 | if (!si->workers && is_udp_based_proto(si->proto)) |
672 | 0 | si->workers = udp_workers_no; |
673 | 0 | if (si->port_no==0) |
674 | 0 | si->port_no= protos[si->proto].default_port; |
675 | |
|
676 | 0 | tmp=int2str(si->port_no, &len); |
677 | 0 | if (len>=MAX_PORT_LEN){ |
678 | 0 | LM_ERR("bad port number: %d\n", si->port_no); |
679 | 0 | return -1; |
680 | 0 | } |
681 | | |
682 | 0 | si->port_no_str.s=(char*)pkg_malloc(len+1); |
683 | 0 | if (si->port_no_str.s==0){ |
684 | 0 | LM_ERR("out of pkg memory.\n"); |
685 | 0 | goto error; |
686 | 0 | } |
687 | 0 | memcpy(si->port_no_str.s, tmp, len+1); |
688 | 0 | si->port_no_str.len=len; |
689 | | /* get "official hostnames", all the aliases etc. */ |
690 | 0 | he=resolvehost(si->name.s,0); |
691 | 0 | if (he==0){ |
692 | 0 | LM_ERR("could not resolve %s\n", si->name.s); |
693 | 0 | goto error; |
694 | 0 | } |
695 | | /* check if we got the official name */ |
696 | 0 | if (strcasecmp(he->h_name, si->name.s)!=0){ |
697 | 0 | if (add_aliases && add_alias(si->name.s, si->name.len, |
698 | 0 | si->port_no, si->proto, ACCEPT_SUBDOMAIN_ALIAS(si->flags))<0){ |
699 | 0 | LM_ERR("add_alias failed\n"); |
700 | 0 | } |
701 | | /* change the official name */ |
702 | 0 | pkg_free(si->name.s); |
703 | 0 | si->name.s=(char*)pkg_malloc(strlen(he->h_name)+1); |
704 | 0 | if (si->name.s==0){ |
705 | 0 | LM_ERR("out of pkg memory.\n"); |
706 | 0 | goto error; |
707 | 0 | } |
708 | 0 | si->name.len=strlen(he->h_name); |
709 | 0 | memcpy(si->name.s, he->h_name, si->name.len+1); |
710 | 0 | } |
711 | | /* add the aliases*/ |
712 | 0 | if (add_aliases) { |
713 | 0 | for(h=he->h_aliases; h && *h; h++) |
714 | 0 | if (add_alias(*h, strlen(*h), si->port_no, si->proto, |
715 | 0 | ACCEPT_SUBDOMAIN_ALIAS(si->flags))<0){ |
716 | 0 | LM_ERR("add_alias failed\n"); |
717 | 0 | } |
718 | 0 | } |
719 | 0 | hostent2ip_addr(&si->address, he, 0); /*convert to ip_addr |
720 | | format*/ |
721 | 0 | if ((tmp=ip_addr2a(&si->address))==0) goto error; |
722 | 0 | if (si->address.af == AF_INET6) { |
723 | 0 | si->address_str.s=(char*)pkg_malloc(strlen(tmp)+1+2); |
724 | 0 | if (si->address_str.s==0){ |
725 | 0 | LM_ERR("out of pkg memory.\n"); |
726 | 0 | goto error; |
727 | 0 | } |
728 | 0 | si->address_str.s[0] = '['; |
729 | 0 | memcpy( si->address_str.s+1 , tmp, strlen(tmp)); |
730 | 0 | si->address_str.s[1+strlen(tmp)] = ']'; |
731 | 0 | si->address_str.s[2+strlen(tmp)] = '\0'; |
732 | 0 | si->address_str.len=strlen(tmp) + 2; |
733 | 0 | } else { |
734 | 0 | si->address_str.s=(char*)pkg_malloc(strlen(tmp)+1); |
735 | 0 | if (si->address_str.s==0){ |
736 | 0 | LM_ERR("out of pkg memory.\n"); |
737 | 0 | goto error; |
738 | 0 | } |
739 | 0 | memcpy(si->address_str.s, tmp, strlen(tmp)+1); |
740 | 0 | si->address_str.len=strlen(tmp); |
741 | 0 | } |
742 | | /* set is_ip (1 if name is an ip address, 0 otherwise) */ |
743 | 0 | if ( auto_aliases && (si->address_str.len==si->name.len) && |
744 | 0 | (strncasecmp(si->address_str.s, si->name.s, |
745 | 0 | si->address_str.len)==0) |
746 | 0 | ){ |
747 | 0 | si->flags|=SI_IS_IP; |
748 | | /* do rev. DNS on it (for aliases)*/ |
749 | 0 | he=rev_resolvehost(&si->address); |
750 | 0 | if (he==0){ |
751 | 0 | LM_WARN("could not rev. resolve %s\n", si->name.s); |
752 | 0 | }else{ |
753 | | /* add the aliases*/ |
754 | 0 | if (add_alias(he->h_name, strlen(he->h_name), |
755 | 0 | si->port_no, si->proto, ACCEPT_SUBDOMAIN_ALIAS(si->flags))<0){ |
756 | 0 | LM_ERR("add_alias failed\n"); |
757 | 0 | } |
758 | 0 | for(h=he->h_aliases; h && *h; h++) |
759 | 0 | if (add_alias(*h,strlen(*h),si->port_no,si->proto, |
760 | 0 | ACCEPT_SUBDOMAIN_ALIAS(si->flags))<0){ |
761 | 0 | LM_ERR(" add_alias failed\n"); |
762 | 0 | } |
763 | 0 | } |
764 | 0 | } |
765 | | |
766 | | /* Now build an ip_addr structure for the adv_name, if there is one |
767 | | * so that find_si can find it later easily. Doing this so that |
768 | | * we can force_send_socket() on an advertised name. Generally there |
769 | | * is little interest in dealing with an advertised name as anything |
770 | | * other than an opaque string that we blindly put into the SIP |
771 | | * message. |
772 | | */ |
773 | 0 | if(si->adv_name_str.len) { |
774 | | /* If adv_name_str is already an IP, this is kinda foolish cus it |
775 | | * converts it to ip_addr, then to he, then here we go back to |
776 | | * ip_addr, but it's either that, or we duplicate the logic to |
777 | | * check for an ip address here, and still we might have to call |
778 | | * resolvehost(). |
779 | | */ |
780 | 0 | he=resolvehost(si->adv_name_str.s,0); |
781 | 0 | if (he==0){ |
782 | 0 | LM_ERR("ERROR: fix_socket_list: could not resolve " |
783 | 0 | "advertised name %s\n", si->adv_name_str.s); |
784 | 0 | goto error; |
785 | 0 | } |
786 | 0 | hostent2ip_addr(&si->adv_address, he, 0); /*convert to ip_addr */ |
787 | |
|
788 | 0 | if (si->adv_address.af == AF_INET6 /* translates to IPv6 */ |
789 | 0 | && str2ip6(&si->adv_name_str)!=NULL /* it's an actual IP */ |
790 | 0 | && si->adv_name_str.s[0]!='[' ) /* not enclosed */ |
791 | 0 | { |
792 | 0 | tmp = pkg_malloc( si->adv_name_str.len +2 ); |
793 | 0 | if (tmp==NULL) { |
794 | 0 | LM_ERR("failed to convert advertized IPv6 " |
795 | 0 | "to enclosed format\n"); |
796 | 0 | goto error; |
797 | 0 | } |
798 | 0 | tmp[0] = '['; |
799 | 0 | memcpy( tmp+1, si->adv_name_str.s, si->adv_name_str.len); |
800 | 0 | tmp[si->adv_name_str.len+1] = ']'; |
801 | 0 | pkg_free(si->adv_name_str.s); |
802 | 0 | si->adv_name_str.s = tmp; |
803 | 0 | si->adv_name_str.len += 2; |
804 | 0 | } |
805 | | |
806 | | /* build and set string encoding for the adv socket info |
807 | | * This is usefful for the usrloc module when it's generating |
808 | | * or updating the socket on a location record, so we'll generate |
809 | | * it up front just like the regular sock_str so we don't have |
810 | | * to worry about it later. |
811 | | */ |
812 | 0 | tmp = socket2str( si, 0, &si->adv_sock_str.len, 1); |
813 | 0 | if (tmp==0) { |
814 | 0 | LM_ERR("ERROR: fix_socket_list: failed to convert " |
815 | 0 | "socket to string (adv)\n"); |
816 | 0 | goto error; |
817 | 0 | } |
818 | 0 | si->adv_sock_str.s=(char*)pkg_malloc(si->adv_sock_str.len); |
819 | 0 | if (si->adv_sock_str.s==0) { |
820 | 0 | LM_ERR("ERROR: fix_socket_list: out of memory.\n"); |
821 | 0 | goto error; |
822 | 0 | } |
823 | 0 | memcpy(si->adv_sock_str.s, tmp, si->adv_sock_str.len); |
824 | 0 | } |
825 | | |
826 | 0 | if (si->tag.len) { |
827 | | /* build and set string encoding for the tagged socket info */ |
828 | 0 | tmp = socket2str( si, 0, &si->tag_sock_str.len, 2); |
829 | 0 | if (tmp==0) { |
830 | 0 | LM_ERR("failed to convert tag socket to string\n"); |
831 | 0 | goto error; |
832 | 0 | } |
833 | 0 | si->tag_sock_str.s=(char*)pkg_malloc(si->tag_sock_str.len); |
834 | 0 | if (si->tag_sock_str.s==0) { |
835 | 0 | LM_ERR("out of pkg memory.\n"); |
836 | 0 | goto error; |
837 | 0 | } |
838 | 0 | memcpy(si->tag_sock_str.s, tmp, si->tag_sock_str.len); |
839 | 0 | } |
840 | | |
841 | | /* build and set string encoding for the real socket info */ |
842 | 0 | tmp = socket2str( si, 0, &si->sock_str.len, 0); |
843 | 0 | if (tmp==0) { |
844 | 0 | LM_ERR("failed to convert socket to string\n"); |
845 | 0 | goto error; |
846 | 0 | } |
847 | 0 | si->sock_str.s=(char*)pkg_malloc(si->sock_str.len); |
848 | 0 | if (si->sock_str.s==0) { |
849 | 0 | LM_ERR("out of pkg memory.\n"); |
850 | 0 | goto error; |
851 | 0 | } |
852 | 0 | memcpy(si->sock_str.s, tmp, si->sock_str.len); |
853 | |
|
854 | | #ifdef USE_MCAST |
855 | | /* Check if it is an multicast address and |
856 | | * set the flag if so |
857 | | */ |
858 | | if (is_mcast(&si->address)) { |
859 | | si->flags |= SI_IS_MCAST; |
860 | | } |
861 | | #endif /* USE_MCAST */ |
862 | |
|
863 | | #ifdef EXTRA_DEBUG |
864 | | printf(" %.*s [%s]:%s%s%s\n", si->name.len, |
865 | | si->name.s, si->address_str.s, si->port_no_str.s, |
866 | | si->flags & SI_IS_MCAST ? " mcast" : "", |
867 | | is_anycast(si) ? " anycast" : ""); |
868 | | #endif |
869 | 0 | return 0; |
870 | 0 | error: |
871 | 0 | return -1; |
872 | 0 | } |
873 | | |
874 | | /* fixes a socket list => resolve addresses, |
875 | | * interface names, fills missing members, remove duplicates */ |
876 | | int fix_socket_list(struct socket_info_full **list) |
877 | 0 | { |
878 | 0 | struct socket_info_full* sif; |
879 | 0 | struct socket_info* si; |
880 | 0 | struct socket_info_full* l; |
881 | 0 | struct socket_info_full* next; |
882 | | |
883 | | /* try to change all the interface names into addresses |
884 | | * --ugly hack */ |
885 | |
|
886 | 0 | for (sif=*list;sif;){ |
887 | 0 | next=sif->next; |
888 | 0 | si = &sif->socket_info; |
889 | | // fix the SI_IS_LO flag for sockets specified by IP/hostname as expand_interface |
890 | | // below will only do it for sockets specified using the network interface name |
891 | 0 | if (is_localhost(si)) |
892 | 0 | si->flags |= SI_IS_LO; |
893 | 0 | if (expand_interface(si, list)!=-1){ |
894 | | /* success => remove current entry (shift the entire array)*/ |
895 | 0 | sock_listrm(list, sif); |
896 | 0 | free_sock_info(sif); |
897 | 0 | } |
898 | 0 | sif=next; |
899 | 0 | } |
900 | | /* get ips & fill the port numbers*/ |
901 | | #ifdef EXTRA_DEBUG |
902 | | LM_DBG("listening on \n"); |
903 | | #endif |
904 | 0 | for (sif=*list;sif;sif=sif->next){ |
905 | 0 | si = &sif->socket_info; |
906 | 0 | if (fix_socket(sif, auto_aliases) < 0) { |
907 | 0 | sock_listrm(list, sif); |
908 | 0 | free_sock_info(sif); |
909 | 0 | } |
910 | 0 | } |
911 | | /* removing duplicate addresses*/ |
912 | 0 | for (sif=*list;sif; sif=sif->next){ |
913 | 0 | for (l=sif->next;l;){ |
914 | 0 | next=l->next; |
915 | 0 | si = &sif->socket_info; |
916 | 0 | const struct socket_info *sl = &l->socket_info; |
917 | 0 | if ((si->port_no==sl->port_no) && |
918 | 0 | (si->address.af==sl->address.af) && |
919 | 0 | (memcmp(si->address.u.addr, sl->address.u.addr, si->address.len) |
920 | 0 | == 0) |
921 | 0 | ){ |
922 | | #ifdef EXTRA_DEBUG |
923 | | printf("removing duplicate %s [%s] == %s [%s]\n", |
924 | | si->name.s, si->address_str.s, |
925 | | sl->name.s, sl->address_str.s); |
926 | | #endif |
927 | | /* add the name to the alias list*/ |
928 | 0 | if ((!(sl->flags& SI_IS_IP)) && ( |
929 | 0 | (sl->name.len!=si->name.len)|| |
930 | 0 | (strncmp(sl->name.s, si->name.s, si->name.len)!=0)) |
931 | 0 | ) |
932 | 0 | if (add_alias(sl->name.s,sl->name.len,sl->port_no,sl->proto, |
933 | 0 | ACCEPT_SUBDOMAIN_ALIAS(sl->flags))<0) |
934 | 0 | LM_ERR(" add_alias failed\n"); |
935 | | |
936 | | /* remove l*/ |
937 | 0 | sock_listrm(list, l); |
938 | 0 | free_sock_info(l); |
939 | 0 | } |
940 | 0 | l=next; |
941 | 0 | } |
942 | 0 | } |
943 | |
|
944 | | #ifdef USE_MCAST |
945 | | /* Remove invalid multicast entries */ |
946 | | sif=*list; |
947 | | while(sif){ |
948 | | si = &sif->socket_info; |
949 | | if ((si->flags & SI_IS_MCAST) && |
950 | | (si->proto != PROTO_UDP) |
951 | | ){ |
952 | | LM_WARN("removing entry %s:%s [%s]:%s\n", |
953 | | get_proto_name(si->proto), si->name.s, |
954 | | si->address_str.s, si->port_no_str.s); |
955 | | l = sif; |
956 | | sif=sif->next; |
957 | | sock_listrm(list, l); |
958 | | free_sock_info(l); |
959 | | } else { |
960 | | sif=sif->next; |
961 | | } |
962 | | } |
963 | | #endif /* USE_MCAST */ |
964 | |
|
965 | 0 | return 0; |
966 | 0 | } |
967 | | |
968 | | |
969 | | |
970 | | |
971 | | |
972 | | /* |
973 | | * This function will retrieve a list of all ip addresses and ports that |
974 | | * OpenSIPS is listening on, with respect to the transport protocol specified |
975 | | * with 'protocol'. |
976 | | * |
977 | | * The first parameter, ipList, is a pointer to a pointer. It will be assigned |
978 | | * a new block of memory holding the IP Addresses and ports being listened to |
979 | | * with respect to 'protocol'. The array maps a 2D array into a 1 dimensional |
980 | | * space, and is layed out as follows: |
981 | | * |
982 | | * The first NUM_IP_OCTETS indices will be the IP address, and the next index |
983 | | * the port. So if NUM_IP_OCTETS is equal to 4 and there are two IP addresses |
984 | | * found, then: |
985 | | * |
986 | | * - ipList[0] will be the first octet of the first ip address |
987 | | * - ipList[3] will be the last octet of the first ip address. |
988 | | * - iplist[4] will be the port of the first ip address |
989 | | * - |
990 | | * - iplist[5] will be the first octet of the first ip address, |
991 | | * - and so on. |
992 | | * |
993 | | * The function will return the number of sockets which were found. This can |
994 | | * be used to index into ipList. |
995 | | * |
996 | | * NOTE: This function assigns a block of memory equal to: |
997 | | * |
998 | | * returnedValue * (NUM_IP_OCTETS + 1) * sizeof(int); |
999 | | * |
1000 | | * Therefore it is CRUCIAL that you free ipList when you are done with |
1001 | | * its contents, to avoid a nasty memory leak. |
1002 | | */ |
1003 | 0 | int get_socket_list_from_proto(unsigned int **ipList, int protocol) { |
1004 | |
|
1005 | 0 | struct socket_info_full *sif; |
1006 | 0 | struct socket_info_full ** list; |
1007 | |
|
1008 | 0 | int num_ip_octets = 4; |
1009 | 0 | int numberOfSockets = 0; |
1010 | 0 | int currentRow = 0; |
1011 | | |
1012 | | /* I hate to use #ifdefs, but this is necessary because of the way |
1013 | | * get_sock_info_list() is defined. */ |
1014 | 0 | if (protocol == PROTO_TCP) |
1015 | 0 | { |
1016 | 0 | return 0; |
1017 | 0 | } |
1018 | | |
1019 | 0 | if (protocol == PROTO_TLS) |
1020 | 0 | { |
1021 | 0 | return 0; |
1022 | 0 | } |
1023 | | |
1024 | | /* Retrieve the list of sockets with respect to the given protocol. */ |
1025 | 0 | list=get_sock_info_list(protocol); |
1026 | | |
1027 | | /* Find out how many sockets are in the list. We need to know this so |
1028 | | * we can malloc an array to assign to ipList. */ |
1029 | 0 | for(sif=list?*list:0; sif; sif=sif->next){ |
1030 | | /* We only support IPV4 at this point. */ |
1031 | 0 | if (sif->socket_info.address.af == AF_INET) { |
1032 | 0 | numberOfSockets++; |
1033 | 0 | } |
1034 | 0 | } |
1035 | | |
1036 | | /* There are no open sockets with respect to the given protocol. */ |
1037 | 0 | if (numberOfSockets == 0) |
1038 | 0 | { |
1039 | 0 | return 0; |
1040 | 0 | } |
1041 | | |
1042 | 0 | *ipList = pkg_malloc(numberOfSockets * |
1043 | 0 | (num_ip_octets + 1) * (int)sizeof(int)); |
1044 | | |
1045 | | /* We couldn't allocate memory for the IP List. So all we can do is |
1046 | | * fail. */ |
1047 | 0 | if (*ipList == NULL) { |
1048 | 0 | LM_ERR("no more pkg memory"); |
1049 | 0 | return 0; |
1050 | 0 | } |
1051 | | |
1052 | | |
1053 | | /* We need to search the list again. So find the front of the list. */ |
1054 | 0 | list=get_sock_info_list(protocol); |
1055 | | |
1056 | | /* Extract out the IP Addresses and ports. */ |
1057 | 0 | for(sif=list?*list:0; sif; sif=sif->next){ |
1058 | 0 | const struct socket_info *si = &sif->socket_info; |
1059 | | |
1060 | | /* We currently only support IPV4. */ |
1061 | 0 | if (si->address.af != AF_INET) { |
1062 | 0 | continue; |
1063 | 0 | } |
1064 | | |
1065 | 0 | (*ipList)[currentRow*(num_ip_octets + 1) ] = |
1066 | 0 | si->address.u.addr[0]; |
1067 | 0 | (*ipList)[currentRow*(num_ip_octets + 1)+1] = |
1068 | 0 | si->address.u.addr[1]; |
1069 | 0 | (*ipList)[currentRow*(num_ip_octets + 1)+2] = |
1070 | 0 | si->address.u.addr[2]; |
1071 | 0 | (*ipList)[currentRow*(num_ip_octets + 1)+3] = |
1072 | 0 | si->address.u.addr[3]; |
1073 | 0 | (*ipList)[currentRow*(num_ip_octets + 1)+4] = |
1074 | 0 | si->port_no; |
1075 | |
|
1076 | 0 | currentRow++; |
1077 | 0 | } |
1078 | |
|
1079 | 0 | return numberOfSockets; |
1080 | 0 | } |
1081 | | |
1082 | | /* |
1083 | | * Takes a 'line' (from the proc file system), parses out the ipAddress, |
1084 | | * address, and stores the number of bytes waiting in 'rx_queue' |
1085 | | * |
1086 | | * Returns 1 on success, and 0 on a failed parse. |
1087 | | * |
1088 | | * Note: The format of ipAddress is as defined in the comments of |
1089 | | * get_socket_list_from_proto() in this file. |
1090 | | * |
1091 | | */ |
1092 | | static int parse_proc_net_line(char *line, unsigned int *ipAddress, int *rx_queue) |
1093 | 0 | { |
1094 | 0 | int i; |
1095 | |
|
1096 | 0 | unsigned int ipOctetExtractionMask = 0xFF; |
1097 | |
|
1098 | 0 | char *currColonLocation; |
1099 | 0 | char *nextNonNumericalChar; |
1100 | 0 | char *currentLocationInLine = line; |
1101 | |
|
1102 | 0 | unsigned int parsedInteger[4]; |
1103 | | |
1104 | | /* Example line from /proc/net/tcp or /proc/net/udp: |
1105 | | * |
1106 | | * sl local_address rem_address st tx_queue rx_queue |
1107 | | * 21: 5A0A0B0A:CAC7 1C016E0A:0016 01 00000000:00000000 |
1108 | | * |
1109 | | * Algorithm: |
1110 | | * |
1111 | | * 1) Find the location of the first ':' |
1112 | | * 2) Parse out the IP Address into an integer |
1113 | | * 3) Find the location of the second ':' |
1114 | | * 4) Parse out the port number. |
1115 | | * 5) Find the location of the fourth ':' |
1116 | | * 6) Parse out the rx_queue. |
1117 | | */ |
1118 | |
|
1119 | 0 | for (i = 0; i < 4; i++) { |
1120 | |
|
1121 | 0 | currColonLocation = strchr(currentLocationInLine, ':'); |
1122 | | |
1123 | | /* We didn't find all the needed ':', so fail. */ |
1124 | 0 | if (currColonLocation == NULL) { |
1125 | 0 | return 0; |
1126 | 0 | } |
1127 | | |
1128 | | /* Parse out the integer, keeping the location of the next |
1129 | | * non-numerical character. */ |
1130 | 0 | parsedInteger[i] = |
1131 | 0 | (int) strtol(++currColonLocation, &nextNonNumericalChar, |
1132 | 0 | 16); |
1133 | | |
1134 | | /* strtol()'s specifications specify that the second parameter |
1135 | | * is set to the first parameter when a number couldn't be |
1136 | | * parsed out. This means the parse was unsuccesful. */ |
1137 | 0 | if (nextNonNumericalChar == currColonLocation) { |
1138 | 0 | return 0; |
1139 | 0 | } |
1140 | | |
1141 | | /* Reset the currentLocationInLine to the last non-numerical |
1142 | | * character, so that next iteration of this loop, we can find |
1143 | | * the next colon location. */ |
1144 | 0 | currentLocationInLine = nextNonNumericalChar; |
1145 | |
|
1146 | 0 | } |
1147 | | |
1148 | | /* Extract out the segments of the IP Address. They are stored in |
1149 | | * reverse network byte order. */ |
1150 | 0 | for (i = 0; i < NUM_IP_OCTETS; i++) { |
1151 | |
|
1152 | 0 | ipAddress[i] = |
1153 | 0 | parsedInteger[0] & (ipOctetExtractionMask << i*8); |
1154 | |
|
1155 | 0 | ipAddress[i] >>= i*8; |
1156 | |
|
1157 | 0 | } |
1158 | |
|
1159 | 0 | ipAddress[NUM_IP_OCTETS] = parsedInteger[1]; |
1160 | |
|
1161 | 0 | *rx_queue = parsedInteger[3]; |
1162 | |
|
1163 | 0 | return 1; |
1164 | |
|
1165 | 0 | } |
1166 | | |
1167 | | |
1168 | | /* |
1169 | | * Returns 1 if ipOne was found in ipArray, and 0 otherwise. |
1170 | | * |
1171 | | * The format of ipOne and ipArray are described in the comments of |
1172 | | * get_socket_list_from_proto() in this file. |
1173 | | * |
1174 | | * */ |
1175 | | static int match_ip_and_port(unsigned int *ipOne, unsigned int *ipArray, int sizeOf_ipArray) |
1176 | 0 | { |
1177 | 0 | int curIPAddrIdx; |
1178 | 0 | int curOctetIdx; |
1179 | 0 | int ipArrayIndex; |
1180 | | |
1181 | | /* Loop over every IP Address */ |
1182 | 0 | for (curIPAddrIdx = 0; curIPAddrIdx < sizeOf_ipArray; curIPAddrIdx++) { |
1183 | | |
1184 | | /* Check for octets that don't match. If one is found, skip the |
1185 | | * rest. */ |
1186 | 0 | for (curOctetIdx = 0; curOctetIdx < NUM_IP_OCTETS + 1; curOctetIdx++) { |
1187 | | |
1188 | | /* We've encoded a 2D array as a 1D array. So find out |
1189 | | * our position in the 1D array. */ |
1190 | 0 | ipArrayIndex = |
1191 | 0 | curIPAddrIdx * (NUM_IP_OCTETS + 1) + curOctetIdx; |
1192 | |
|
1193 | 0 | if (ipOne[curOctetIdx] != ipArray[ipArrayIndex]) { |
1194 | 0 | break; |
1195 | 0 | } |
1196 | 0 | } |
1197 | | |
1198 | | /* If the index from the inner loop is equal to NUM_IP_OCTETS |
1199 | | * + 1, then that means that every octet (and the port with the |
1200 | | * + 1) matched. */ |
1201 | 0 | if (curOctetIdx == NUM_IP_OCTETS + 1) { |
1202 | 0 | return 1; |
1203 | 0 | } |
1204 | |
|
1205 | 0 | } |
1206 | | |
1207 | 0 | return 0; |
1208 | 0 | } |
1209 | | |
1210 | | |
1211 | | /* |
1212 | | * Returns the number of bytes waiting to be consumed on the network interfaces |
1213 | | * assigned the IP Addresses specified in interfaceList. The check will be |
1214 | | * limited to the TCP or UDP transport exclusively. Specifically: |
1215 | | * |
1216 | | * - If forTCP is non-zero, the check involves only the TCP transport. |
1217 | | * - if forTCP is zero, the check involves only the UDP transport. |
1218 | | * |
1219 | | * Note: This only works on linux systems supporting the /proc/net/[tcp|udp] |
1220 | | * interface. On other systems, zero will always be returned. |
1221 | | */ |
1222 | | static int get_used_waiting_queue( |
1223 | | int forTCP, unsigned int *interfaceList, int listSize) |
1224 | 0 | { |
1225 | 0 | FILE *fp; |
1226 | 0 | char *fileToOpen; |
1227 | |
|
1228 | 0 | char lineBuffer[MAX_PROC_BUFFER]; |
1229 | 0 | unsigned int ipAddress[NUM_IP_OCTETS+1]; |
1230 | 0 | int rx_queue; |
1231 | 0 | int waitingQueueSize = 0; |
1232 | |
|
1233 | 0 | if (listSize==0 || interfaceList==NULL) |
1234 | 0 | return 0; |
1235 | | |
1236 | | /* Set up the file we want to open. */ |
1237 | 0 | if (forTCP) { |
1238 | 0 | fileToOpen = "/proc/net/tcp"; |
1239 | 0 | } else { |
1240 | 0 | fileToOpen = "/proc/net/udp"; |
1241 | 0 | } |
1242 | |
|
1243 | 0 | fp = fopen(fileToOpen, "r"); |
1244 | |
|
1245 | 0 | if (fp == NULL) { |
1246 | 0 | LM_DBG("Could not open %s. openserMsgQueu eDepth and its related" |
1247 | 0 | " alarms will not be available.\n", fileToOpen); |
1248 | 0 | return 0; |
1249 | 0 | } |
1250 | | |
1251 | | /* Read in every line of the file, parse out the ip address, port, and |
1252 | | * rx_queue, and compare to our list of interfaces we are listening on. |
1253 | | * Add up rx_queue for those lines which match our known interfaces. */ |
1254 | 0 | while (fgets(lineBuffer, MAX_PROC_BUFFER, fp)!=NULL) { |
1255 | | |
1256 | | /* Parse out the ip address, port, and rx_queue. */ |
1257 | 0 | if(parse_proc_net_line(lineBuffer, ipAddress, &rx_queue)) { |
1258 | | |
1259 | | /* Only add rx_queue if the line just parsed corresponds |
1260 | | * to an interface we are listening on. We do this |
1261 | | * check because it is possible that this system has |
1262 | | * other network interfaces that OpenSER has been told |
1263 | | * to ignore. */ |
1264 | 0 | if (match_ip_and_port(ipAddress, interfaceList, listSize)) { |
1265 | 0 | waitingQueueSize += rx_queue; |
1266 | 0 | } |
1267 | 0 | } |
1268 | 0 | } |
1269 | |
|
1270 | 0 | fclose(fp); |
1271 | |
|
1272 | 0 | return waitingQueueSize; |
1273 | 0 | } |
1274 | | |
1275 | | /* |
1276 | | * Returns the sum of the number of bytes waiting to be consumed on all network |
1277 | | * interfaces and transports that OpenSIPS is listening on. |
1278 | | * |
1279 | | * Note: This currently only works on systems supporting the /proc/net/[tcp|udp] |
1280 | | * interface. On other systems, zero will always be returned. To change |
1281 | | * this in the future, add an equivalent for get_used_waiting_queue(). |
1282 | | */ |
1283 | | int get_total_bytes_waiting(int only_proto) |
1284 | 0 | { |
1285 | 0 | static unsigned int *UDPList = NULL; |
1286 | 0 | static unsigned int *TCPList = NULL; |
1287 | 0 | static unsigned int *TLSList = NULL; |
1288 | |
|
1289 | 0 | static int numUDPSockets = -1; |
1290 | 0 | static int numTCPSockets = -1; |
1291 | 0 | static int numTLSSockets = -1; |
1292 | |
|
1293 | 0 | int bytesWaiting = 0; |
1294 | | |
1295 | | /* Extract out the IP address address for UDP, TCP, and TLS, keeping |
1296 | | * track of the number of IP addresses from each transport */ |
1297 | 0 | if (numUDPSockets==-1) |
1298 | 0 | numUDPSockets = get_socket_list_from_proto(&UDPList, PROTO_UDP); |
1299 | 0 | if (numTCPSockets==-1) |
1300 | 0 | numTCPSockets = get_socket_list_from_proto(&TCPList, PROTO_TCP); |
1301 | 0 | if (numTLSSockets==-1) |
1302 | 0 | numTLSSockets = get_socket_list_from_proto(&TLSList, PROTO_TLS); |
1303 | | |
1304 | | /* Find out the number of bytes waiting on our interface list over all |
1305 | | * UDP and TCP transports. */ |
1306 | 0 | if (only_proto==PROTO_NONE) { |
1307 | 0 | bytesWaiting += get_used_waiting_queue(0, UDPList, numUDPSockets); |
1308 | 0 | bytesWaiting += get_used_waiting_queue(1, TCPList, numTCPSockets); |
1309 | 0 | bytesWaiting += get_used_waiting_queue(1, TLSList, numTLSSockets); |
1310 | 0 | } else if (only_proto==PROTO_UDP) { |
1311 | 0 | bytesWaiting += get_used_waiting_queue(0, UDPList, numUDPSockets); |
1312 | 0 | } else if (only_proto==PROTO_TCP) { |
1313 | 0 | bytesWaiting += get_used_waiting_queue(1, TCPList, numTCPSockets); |
1314 | 0 | } else if (only_proto==PROTO_TLS) { |
1315 | 0 | bytesWaiting += get_used_waiting_queue(1, TLSList, numTLSSockets); |
1316 | 0 | } |
1317 | |
|
1318 | 0 | return bytesWaiting; |
1319 | 0 | } |
1320 | | |
1321 | | |
1322 | | |
1323 | | void print_aliases(void) |
1324 | 0 | { |
1325 | 0 | struct host_alias* a; |
1326 | |
|
1327 | 0 | for(a=aliases; a; a=a->next) |
1328 | 0 | if (a->port) |
1329 | 0 | printf(" %s: %.*s:%d\n", get_proto_name(a->proto), |
1330 | 0 | a->alias.len, a->alias.s, a->port); |
1331 | 0 | else |
1332 | 0 | printf(" %s: %.*s:*\n", get_proto_name(a->proto), |
1333 | 0 | a->alias.len, a->alias.s); |
1334 | 0 | } |
1335 | | |
1336 | | /* |
1337 | | * Arguments : |
1338 | | * sock - socket to have buffer increased |
1339 | | * buff_choice - 0 for receive buff, 1 for send buff |
1340 | | * buff_max - max size of socket buffer we are looking for |
1341 | | * buff_increment - increment nr of bytes after reaching limit |
1342 | | * |
1343 | | * Returns : |
1344 | | * 0 in case of success |
1345 | | * 1 in case of failure |
1346 | | */ |
1347 | | int probe_max_sock_buff(int sock,int buff_choice,int buff_max,int buff_increment) |
1348 | 0 | { |
1349 | 0 | unsigned int optval, ioptval, ioptvallen, foptval, foptvallen, voptval, voptvallen; |
1350 | 0 | int phase=0; |
1351 | 0 | int buff_opt; |
1352 | 0 | char *info; |
1353 | |
|
1354 | 0 | if (buff_choice == 0) |
1355 | 0 | { |
1356 | 0 | info = "rcv"; |
1357 | 0 | buff_opt = SO_RCVBUF; |
1358 | 0 | } |
1359 | 0 | else if (buff_choice == 1) |
1360 | 0 | { |
1361 | 0 | info = "snd"; |
1362 | 0 | buff_opt = SO_SNDBUF; |
1363 | 0 | } |
1364 | 0 | else |
1365 | 0 | { |
1366 | 0 | LM_WARN("Called with unimplemented buff_choice - %d\n",buff_choice); |
1367 | 0 | return 1; |
1368 | 0 | } |
1369 | | |
1370 | | /* try to increase buffer size as much as we can */ |
1371 | 0 | ioptvallen=sizeof(ioptval); |
1372 | 0 | if (getsockopt( sock, SOL_SOCKET, buff_opt, (void*) &ioptval, |
1373 | 0 | &ioptvallen) == -1 ) |
1374 | 0 | { |
1375 | 0 | LM_ERR("getsockopt: %s\n", strerror(errno)); |
1376 | 0 | return -1; |
1377 | 0 | } |
1378 | 0 | if ( ioptval==0 ) |
1379 | 0 | { |
1380 | 0 | LM_DBG(" getsockopt: %s initially set to 0; resetting to %d\n", |
1381 | 0 | info,buff_increment ); |
1382 | 0 | ioptval=buff_increment; |
1383 | 0 | } else LM_DBG("getsockopt: %s is initially %d\n",info, ioptval ); |
1384 | 0 | for (optval=ioptval; ; ) { |
1385 | | /* increase size; double in initial phase, add linearly later */ |
1386 | 0 | if (phase==0) optval <<= 1; else optval+=buff_increment; |
1387 | 0 | if (optval > maxbuffer){ |
1388 | 0 | if (phase==1) break; |
1389 | 0 | else { phase=1; optval >>=1; continue; } |
1390 | 0 | } |
1391 | | /* LM_DBG("trying : %d\n", optval ); */ |
1392 | 0 | if (setsockopt( sock, SOL_SOCKET, buff_opt, |
1393 | 0 | (void*)&optval, sizeof(optval)) ==-1){ |
1394 | | /* Solaris returns -1 if asked size too big; Linux ignores */ |
1395 | 0 | LM_DBG("setsockopt: SOL_SOCKET failed" |
1396 | 0 | " for %d, phase %d: %s\n", optval, phase, strerror(errno)); |
1397 | | /* if setting buffer size failed and still in the aggressive |
1398 | | phase, try less aggressively; otherwise give up */ |
1399 | 0 | if (phase==0) { phase=1; optval >>=1 ; continue; } |
1400 | 0 | else break; |
1401 | 0 | } |
1402 | | /* verify if change has taken effect */ |
1403 | | /* Linux note -- otherwise I would never know that; funny thing: Linux |
1404 | | doubles size for which we asked in setsockopt */ |
1405 | 0 | voptvallen=sizeof(voptval); |
1406 | 0 | if (getsockopt( sock, SOL_SOCKET, buff_opt, (void*) &voptval, |
1407 | 0 | &voptvallen) == -1 ) |
1408 | 0 | { |
1409 | 0 | LM_ERR("getsockopt: %s\n", strerror(errno)); |
1410 | 0 | return -1; |
1411 | 0 | } else { |
1412 | | /*LM_DBG("setting %s: set=%d,verify=%d\n",info, |
1413 | | optval, voptval);*/ |
1414 | 0 | if (voptval<optval) { |
1415 | 0 | LM_DBG("setting %s buf to %d had no effect\n",info,optval); |
1416 | | /* if setting buffer size failed and still in the aggressive |
1417 | | phase, try less aggressively; otherwise give up */ |
1418 | 0 | if (phase==0) { phase=1; optval >>=1 ; continue; } |
1419 | 0 | else break; |
1420 | 0 | } |
1421 | 0 | } |
1422 | |
|
1423 | 0 | } /* for ... */ |
1424 | 0 | foptvallen=sizeof(foptval); |
1425 | 0 | if (getsockopt( sock, SOL_SOCKET, buff_opt, (void*) &foptval, |
1426 | 0 | &foptvallen) == -1 ) |
1427 | 0 | { |
1428 | 0 | LM_ERR("getsockopt: %s\n", strerror(errno)); |
1429 | 0 | return -1; |
1430 | 0 | } |
1431 | 0 | LM_DBG("using %s buffer of %d kb\n",info, (foptval/1024)); |
1432 | |
|
1433 | 0 | return 0; |
1434 | 0 | } |
1435 | | |
1436 | | struct socket_id *socket_info2id(struct socket_info *si) |
1437 | 0 | { |
1438 | 0 | static struct socket_id sid; |
1439 | |
|
1440 | 0 | memset(&sid, 0, sizeof sid); |
1441 | 0 | sid.name = si->name.s; |
1442 | 0 | sid.adv_name = si->adv_sock_str.s; |
1443 | 0 | sid.tag = si->tag.s; |
1444 | 0 | if (si->s_profile) |
1445 | 0 | sid.auto_scaling_profile = si->s_profile->name; |
1446 | 0 | sid.adv_port = si->adv_port; |
1447 | 0 | sid.proto = si->proto; |
1448 | 0 | sid.port = si->port_no; |
1449 | 0 | sid.workers = si->workers; |
1450 | 0 | return &sid; |
1451 | 0 | } |