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