📄 test_nat.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2005 Nokia Corporation. * * Contact: Pekka Pessi <pekka.pessi@nokia.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * *//**@CFILE test_nat.c * @brief Simulated NAT for testing nua * * NAT thing works so that we set the outgoing proxy URI to point * towards its "private" address and give the real address of the proxy * as its "public" address. If we use different IP families here, we may * even manage to test real connectivity problems as proxy and endpoint * can not talk to each other. * * @author Pekka Pessi <Pekka.Pessi@nokia.com> * * @date Created: Wed Mar 8 19:54:28 EET 2006 */#include "config.h"#include <string.h>struct nat;struct binding;#define SU_ROOT_MAGIC_T struct nat#define SU_WAKEUP_ARG_T struct binding#include <sofia-sip/su_wait.h>#include <sofia-sip/su_tagarg.h>#include <sofia-sip/su_localinfo.h>#include <sofia-sip/su_log.h>#include <stdlib.h>#include <assert.h>#if !defined(EADDRNOTAVAIL) && defined(_WIN32)#define EADDRNOTAVAIL WSAEADDRNOTAVAIL#endif#define LIST_PROTOS(STORAGE, PREFIX, T) \STORAGE void PREFIX ##_insert(T **list, T *node), \ PREFIX ##_remove(T *node)#define LIST_BODIES(STORAGE, PREFIX, T, NEXT, PREV) \STORAGE void PREFIX ##_insert(T **list, T *node) \{ \ if ((node->NEXT = *list)) { \ node->PREV = node->NEXT->PREV; \ node->NEXT->PREV = &node->NEXT; \ } \ else \ node->PREV = list; \ *list = node; \} \STORAGE void PREFIX ##_remove(T *node) \{ \ if (node->PREV) \ if ((*node->PREV = node->NEXT)) \ node->NEXT->PREV = node->PREV; \ node->PREV = NULL; \} \extern int LIST_DUMMY_VARIABLE#include <test_nat.h>struct nat { su_home_t home[1]; su_root_t *parent; su_clone_r clone; tagi_t *tags; su_root_t *root; struct binding *bindings; /* True if we act in symmetric way */ int symmetric; /* Everything sent to in_address will be forwarded to out_address */ su_sockaddr_t in_address[1], out_address[1]; socklen_t in_addrlen, out_addrlen; int family; /* Preferred private family */ /* ...but source address will be "fake" */ su_localinfo_t *localinfo, *private, *fake; int udp_socket, tcp_socket; int udp_register, tcp_register; char buffer[65536];};LIST_PROTOS(static, nat_binding, struct binding);struct binding{ struct binding *next, **prev; struct nat *nat; /* backpointer */ int in_socket, out_socket; int in_register, out_register; int in_closed, out_closed; char in_name[64], out_name[64];};static struct binding *nat_binding_new(struct nat *, int, int);static void nat_binding_destroy(struct binding *);static int new_udp(struct nat *, su_wait_t *wait, struct binding *dummy);static int udp_in_to_out(struct nat *, su_wait_t *wait, struct binding *);static int udp_out_to_in(struct nat *, su_wait_t *wait, struct binding *);static int new_tcp(struct nat *, su_wait_t *wait, struct binding *dummy);static int tcp_in_to_out(struct nat *, su_wait_t *wait, struct binding *);static int tcp_out_to_in(struct nat *, su_wait_t *wait, struct binding *);/* nat entry point */static inttest_nat_init(su_root_t *root, struct nat *nat){ su_localinfo_t *li, hints[1] = {{ 0 }}; int error; unsigned port = 0, port0 = 0; su_sockaddr_t *su; socklen_t sulen; su_wait_t wait[1]; nat->root = root; nat->udp_socket = -1, nat->tcp_socket = -1; error = su_getlocalinfo(hints, &nat->localinfo); if (error) { fprintf(stderr, "test_nat: su_getlocalinfo: %s\n", su_gli_strerror(error)); return -1; } /* We must have two different IP addresses. */ if (!nat->localinfo || !nat->localinfo->li_next) { fprintf(stderr, "test_nat: only one IP address available\n"); return -1; } for (li = nat->localinfo; li; li = li->li_next) { if (nat->family == 0 || nat->family == li->li_family) break; } if (li == NULL) li = nat->localinfo; su = memcpy(nat->in_address, li->li_addr, sulen = li->li_addrlen); nat->private = li; /* Bind TCP and UDP to same port */ for (;;) { nat->udp_socket = su_socket(li->li_family, SOCK_DGRAM, IPPROTO_UDP); if (nat->udp_socket == -1) return -1; if (bind(nat->udp_socket, (void *)su, sulen) < 0) { if (port0 == 0) { su_perror("nat: bind(udp_socket)"); return -1; } fprintf(stderr, "test_nat: port %u: %s\n", port, su_strerror(su_errno())); su_close(nat->udp_socket); nat->udp_socket = -1; if (++port > 65535) port = 1024; if (port == port0) { fprintf(stderr, "test_nat: could not find free port pairt\n"); return -1; } continue; } if (getsockname(nat->udp_socket, (void *)su, &sulen) < 0) { su_perror("nat: bind(udp_socket)"); return -1; } if (port0 == 0) { port = port0 = ntohs(su->su_port); if (port0 == 0) { fprintf(stderr, "test_nat: bind did not return port\n"); return -1; } } nat->tcp_socket = su_socket(li->li_family, SOCK_STREAM, IPPROTO_TCP); if (nat->tcp_socket == -1) return -1; if (bind(nat->tcp_socket, (void *)su, sulen) < 0) { su_close(nat->tcp_socket); nat->tcp_socket = -1; fprintf(stderr, "test_nat: port %u: %s\n", port, su_strerror(su_errno())); if (++port > 65535) port = 1024; if (port == port0) { fprintf(stderr, "test_nat: could not find free port pair\n"); return -1; } continue; } break; } nat->in_addrlen = sulen; if (su_setreuseaddr(nat->udp_socket, 1) < 0) { su_perror("nat: su_setreuseaddr(udp_socket)"); return -1; } if (listen(nat->tcp_socket, 5) < 0) { su_perror("nat: listen(tcp_socket)"); return -1; } if (su_wait_create(wait, nat->udp_socket, SU_WAIT_IN) < 0) { su_perror("nat: su_wait_create"); return -1; } nat->udp_register = su_root_register(root, wait, new_udp, NULL, 0); if (nat->udp_register < 0) { su_perror("nat: su_root_register"); return -1; } if (su_wait_create(wait, nat->tcp_socket, SU_WAIT_IN) < 0) { su_perror("nat: su_wait_create"); return -1; } nat->tcp_register = su_root_register(root, wait, new_tcp, NULL, 0); if (nat->tcp_register < 0) { su_perror("nat: su_root_register"); return -1; } return 0;}static voidtest_nat_deinit(su_root_t *root, struct nat *nat){ struct binding *b; for (b = nat->bindings; b; b = b->next) { if (b->in_register) su_root_deregister(root, b->in_register); su_close(b->in_socket); if (b->out_register) su_root_deregister(root, b->in_register); su_close(b->out_socket); } if (nat->udp_socket != -1) su_close(nat->udp_socket); if (nat->tcp_socket != -1) su_close(nat->tcp_socket); su_freelocalinfo(nat->localinfo); free(nat->tags);}struct nat *test_nat_create(su_root_t *root, int family, tag_type_t tag, tag_value_t value, ...){ struct nat *nat = su_home_new(sizeof *nat); if (nat) { ta_list ta; nat->parent = root; nat->family = family; ta_start(ta, tag, value); nat->tags = tl_llist(ta_tags(ta)); ta_end(ta); if (su_clone_start(root, nat->clone, nat, test_nat_init, test_nat_deinit) == -1) su_home_unref(nat->home), nat = NULL; } return nat;}void test_nat_destroy(struct nat *nat){ if (nat) { su_clone_wait(nat->parent, nat->clone); su_home_unref(nat->home); }}/** Get "private" address. */int test_nat_private(struct nat *nat, void *address, int *return_addrlen){ if (nat == NULL || address == NULL || return_addrlen == NULL) return su_seterrno(EFAULT); if (*return_addrlen < nat->in_addrlen) return su_seterrno(EINVAL); memcpy(address, nat->in_address, *return_addrlen = nat->in_addrlen); return 0;}/** Set "public" address. */int test_nat_public(struct nat *nat, void const *address, int addrlen){ su_sockaddr_t const *su = address; su_localinfo_t *li; if (nat == NULL) return su_seterrno(EFAULT); if (address == NULL) { nat->fake = NULL; return 0; } if (addrlen > sizeof nat->out_address) return su_seterrno(EINVAL); for (li = nat->localinfo; li; li = li->li_next) { if (li != nat->private && li->li_scope == LI_SCOPE_HOST && li->li_family == su->su_family) break; } if (li == NULL) for (li = nat->localinfo; li; li = li->li_next) { if (li != nat->private && li->li_family == su->su_family) break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -