James Kuszmaul | 82f6c04 | 2021-01-17 11:30:16 -0800 | [diff] [blame^] | 1 | /** |
| 2 | * @file genalg.c Detecting Generic ALGs |
| 3 | * |
| 4 | * Copyright (C) 2010 Creytiv.com |
| 5 | */ |
| 6 | #include <re_types.h> |
| 7 | #include <re_mbuf.h> |
| 8 | #include <re_fmt.h> |
| 9 | #include <re_mem.h> |
| 10 | #include <re_sa.h> |
| 11 | #include <re_list.h> |
| 12 | #include <re_stun.h> |
| 13 | #include <re_natbd.h> |
| 14 | |
| 15 | |
| 16 | #define DEBUG_MODULE "natbd_genalg" |
| 17 | #define DEBUG_LEVEL 7 |
| 18 | #include <re_dbg.h> |
| 19 | |
| 20 | |
| 21 | /* |
| 22 | Detecting Generic ALGs |
| 23 | |
| 24 | A number of NAT boxes are now being deployed into the market which |
| 25 | try to provide "generic" ALG functionality. These generic ALGs hunt |
| 26 | for IP addresses, either in text or binary form within a packet, and |
| 27 | rewrite them if they match a binding. This behavior can be detected |
| 28 | because the STUN server returns both the MAPPED-ADDRESS and XOR- |
| 29 | MAPPED-ADDRESS in the same response. If the result in the two does |
| 30 | not match, there a NAT with a generic ALG in the path. |
| 31 | */ |
| 32 | |
| 33 | |
| 34 | /** Defines a NAT Generic ALG detection session */ |
| 35 | struct nat_genalg { |
| 36 | struct stun *stun; /**< STUN Client */ |
| 37 | struct sa srv; /**< Server address and port */ |
| 38 | int proto; /**< IP protocol */ |
| 39 | nat_genalg_h *h; /**< Result handler */ |
| 40 | void *arg; /**< Handler argument */ |
| 41 | }; |
| 42 | |
| 43 | |
| 44 | static void stun_response_handler(int err, uint16_t scode, const char *reason, |
| 45 | const struct stun_msg *msg, void *arg) |
| 46 | { |
| 47 | struct stun_attr *xmap, *map; |
| 48 | struct nat_genalg *ng = arg; |
| 49 | int status = 0; |
| 50 | (void)reason; |
| 51 | |
| 52 | if (err) { |
| 53 | ng->h(err, 0, NULL, -1, NULL, ng->arg); |
| 54 | return; |
| 55 | } |
| 56 | |
| 57 | switch (scode) { |
| 58 | |
| 59 | case 0: |
| 60 | map = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR); |
| 61 | xmap = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); |
| 62 | if (!map || !xmap) { |
| 63 | ng->h(EINVAL, scode, reason, -1, NULL, ng->arg); |
| 64 | break; |
| 65 | } |
| 66 | |
| 67 | status = sa_cmp(&map->v.sa, &xmap->v.sa, SA_ALL) ? -1 : 1; |
| 68 | |
| 69 | ng->h(0, scode, reason, status, &xmap->v.sa, ng->arg); |
| 70 | break; |
| 71 | |
| 72 | default: |
| 73 | ng->h(0, scode, reason, -1, NULL, ng->arg); |
| 74 | break; |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | |
| 79 | static void genalg_destructor(void *data) |
| 80 | { |
| 81 | struct nat_genalg *ng = data; |
| 82 | |
| 83 | mem_deref(ng->stun); |
| 84 | } |
| 85 | |
| 86 | |
| 87 | /** |
| 88 | * Allocate a new NAT Generic ALG detection session |
| 89 | * |
| 90 | * @param ngp Pointer to allocated NAT Generic ALG object |
| 91 | * @param srv STUN Server IP address and port |
| 92 | * @param proto Transport protocol |
| 93 | * @param conf STUN configuration (Optional) |
| 94 | * @param gh Generic ALG handler |
| 95 | * @param arg Handler argument |
| 96 | * |
| 97 | * @return 0 if success, errorcode if failure |
| 98 | */ |
| 99 | int nat_genalg_alloc(struct nat_genalg **ngp, const struct sa *srv, int proto, |
| 100 | const struct stun_conf *conf, |
| 101 | nat_genalg_h *gh, void *arg) |
| 102 | { |
| 103 | struct nat_genalg *ng; |
| 104 | int err; |
| 105 | |
| 106 | if (!ngp || !srv || !proto || !gh) |
| 107 | return EINVAL; |
| 108 | |
| 109 | ng = mem_zalloc(sizeof(*ng), genalg_destructor); |
| 110 | if (!ng) |
| 111 | return ENOMEM; |
| 112 | |
| 113 | err = stun_alloc(&ng->stun, conf, NULL, NULL); |
| 114 | if (err) |
| 115 | goto out; |
| 116 | |
| 117 | sa_cpy(&ng->srv, srv); |
| 118 | ng->proto = proto; |
| 119 | ng->h = gh; |
| 120 | ng->arg = arg; |
| 121 | |
| 122 | out: |
| 123 | if (err) |
| 124 | mem_deref(ng); |
| 125 | else |
| 126 | *ngp = ng; |
| 127 | |
| 128 | return err; |
| 129 | } |
| 130 | |
| 131 | |
| 132 | /** |
| 133 | * Start the NAT Generic ALG detection |
| 134 | * |
| 135 | * @param ng NAT Generic ALG object |
| 136 | * |
| 137 | * @return 0 if success, errorcode if failure |
| 138 | */ |
| 139 | int nat_genalg_start(struct nat_genalg *ng) |
| 140 | { |
| 141 | int err; |
| 142 | |
| 143 | if (!ng) |
| 144 | return EINVAL; |
| 145 | |
| 146 | err = stun_request(NULL, ng->stun, ng->proto, NULL, &ng->srv, 0, |
| 147 | STUN_METHOD_BINDING, NULL, 0, false, |
| 148 | stun_response_handler, ng, 1, |
| 149 | STUN_ATTR_SOFTWARE, stun_software); |
| 150 | |
| 151 | return err; |
| 152 | } |