1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
#include "control_udp.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <pcre2.h>
#include <glib.h>
#include <time.h>
#include <netinet/in.h>
#include <errno.h>
#include "poller.h"
#include "helpers.h"
#include "log.h"
#include "call.h"
#include "udp_listener.h"
#include "call_interfaces.h"
#include "socket.h"
#include "log_funcs.h"
static void control_udp_incoming(struct obj *obj, struct udp_buffer *udp_buf) {
struct control_udp *u = (void *) obj;
int ret;
char **out;
struct iovec iov[10];
unsigned int iovlen;
str cookie, reply = STR_NULL;
cache_entry *ce;
pcre2_match_data *md = pcre2_match_data_create(30, NULL);
ret = pcre2_match(u->parse_re, (PCRE2_SPTR8) udp_buf->str.s, udp_buf->str.len, 0, 0, md, NULL);
if (ret <= 0) {
ret = pcre2_match(u->fallback_re, (PCRE2_SPTR8) udp_buf->str.s, udp_buf->str.len, 0, 0, md, NULL);
if (ret <= 0) {
ilogs(control, LOG_WARNING, "Unable to parse command line from udp:%s: %.*s", udp_buf->addr, STR_FMT(&udp_buf->str));
pcre2_match_data_free(md);
return;
}
ilogs(control, LOG_WARNING, "Failed to properly parse UDP command line '%.*s' from %s, using fallback RE", STR_FMT(&udp_buf->str), udp_buf->addr);
pcre2_substring_list_get(md, (PCRE2_UCHAR ***) &out, NULL);
iov[0].iov_base = (void *) out[RE_UDP_COOKIE];
iov[0].iov_len = strlen(out[RE_UDP_COOKIE]);
if (out[RE_UDP_UL_CMD] && (chrtoupper(out[RE_UDP_UL_CMD][0]) == 'U' || chrtoupper(out[RE_UDP_UL_CMD][0]) == 'L')) {
iov[1].iov_base = (void *) out[4];
iov[1].iov_len = strlen(out[4]);
iov[2].iov_base = (void *) out[3];
iov[2].iov_len = strlen(out[3]);
iov[3].iov_base = "\n";
iov[3].iov_len = 1;
iovlen = 4;
}
else {
iov[1].iov_base = " E8\n";
iov[1].iov_len = 4;
iovlen = 2;
}
socket_sendiov(udp_buf->listener, iov, iovlen, &udp_buf->sin, &udp_buf->local_addr);
pcre2_substring_list_free((SUBSTRING_FREE_ARG) out);
pcre2_match_data_free(md);
return;
}
ilogs(control, LOG_INFO, "Got valid command from udp:%s: %.*s", udp_buf->addr, STR_FMT(&udp_buf->str));
pcre2_substring_list_get(md, (PCRE2_UCHAR ***) &out, NULL);
cookie = STR(out[RE_UDP_COOKIE]);
ce = cookie_cache_lookup(&u->cookie_cache, &cookie);
if (ce) {
reply = ce->reply;
ilogs(control, LOG_INFO, "Detected command from udp:%s as a duplicate", udp_buf->addr);
socket_sendto_from(udp_buf->listener, reply.s, reply.len, &udp_buf->sin, &udp_buf->local_addr);
cache_entry_free(ce);
goto out;
}
if (out[RE_UDP_UL_CALLID])
log_info_c_string(out[RE_UDP_UL_CALLID]);
else if (out[RE_UDP_DQ_CALLID])
log_info_c_string(out[RE_UDP_DQ_CALLID]);
if (chrtoupper(out[RE_UDP_UL_CMD][0]) == 'U')
reply = call_update_udp(out, udp_buf->addr);
else if (chrtoupper(out[RE_UDP_UL_CMD][0]) == 'L')
reply = call_lookup_udp(out);
else if (chrtoupper(out[RE_UDP_DQ_CMD][0]) == 'D')
reply = call_delete_udp(out);
else if (chrtoupper(out[RE_UDP_DQ_CMD][0]) == 'Q')
reply = call_query_udp(out);
else if (chrtoupper(out[RE_UDP_V_CMD][0]) == 'V') {
iovlen = 2;
iov[0].iov_base = (void *) out[RE_UDP_COOKIE];
iov[0].iov_len = strlen(out[RE_UDP_COOKIE]);
iov[1].iov_base = " ";
iov[1].iov_len = 1;
if (chrtoupper(out[RE_UDP_V_FLAGS][0]) == 'F') {
ret = 0;
if (!strcmp(out[RE_UDP_V_PARMS], "20040107"))
ret = 1;
else if (!strcmp(out[RE_UDP_V_PARMS], "20050322"))
ret = 1;
else if (!strcmp(out[RE_UDP_V_PARMS], "20060704"))
ret = 1;
iov[2].iov_base = ret ? "1\n" : "0\n";
iov[2].iov_len = 2;
iovlen++;
}
else {
iov[2].iov_base = "20040107\n";
iov[2].iov_len = 9;
iovlen++;
}
socket_sendiov(udp_buf->listener, iov, iovlen, &udp_buf->sin, &udp_buf->local_addr);
}
if (reply.len) {
socket_sendto_from(udp_buf->listener, reply.s, reply.len, &udp_buf->sin, &udp_buf->local_addr);
str callid = STR_NULL;
cache_entry new_ce = {.reply = reply, .callid = callid};
cookie_cache_insert(&u->cookie_cache, &cookie, &new_ce);
g_free(reply.s);
}
else
cookie_cache_remove(&u->cookie_cache, &cookie);
out:
pcre2_substring_list_free((SUBSTRING_FREE_ARG) out);
pcre2_match_data_free(md);
log_info_pop();
}
void control_udp_free(struct control_udp *u) {
pcre2_code_free(u->parse_re);
pcre2_code_free(u->fallback_re);
close_socket(&u->udp_listener);
cookie_cache_cleanup(&u->cookie_cache);
}
struct control_udp *control_udp_new(const endpoint_t *ep) {
struct control_udp *c;
PCRE2_SIZE erroff;
int errcode;
c = obj_alloc0(struct control_udp, control_udp_free);
c->parse_re = pcre2_compile(
/* cookie cmd flags callid viabranch:5 */
(PCRE2_SPTR8) "^(\\S+)\\s+(?:([ul])(\\S*)\\s+([^;]+)(?:;(\\S+))?\\s+" \
/* addr4 addr6:7 */
"(?:([\\d.]+)|([\\da-f:]+(?::ffff:[\\d.]+)?))" \
/* port fromtag num totag:11 */
"\\s+(\\d+)\\s+(\\S+?);(\\d+)(?:\\s+(\\S+?);\\d+(?:\\s+.*)?)?\r?\n?$" \
/* "d/q" flags callid viabranch fromtag totag:17 */
"|([dq])(\\S*)\\s+([^;\\s]+)(?:;(\\S+))?\\s+(\\S+?)(?:;\\d+)?(?:\\s+(\\S+?)(?:;\\d+)?)?\r?\n?$" \
/* v flags params:20 */
"|(v)(\\S*)(?:\\s+(\\S+))?)",
PCRE2_ZERO_TERMINATED,
PCRE2_DOLLAR_ENDONLY | PCRE2_DOTALL | PCRE2_CASELESS, &errcode, &erroff, NULL);
/* cookie cmd flags callid addr port */
c->fallback_re = pcre2_compile((PCRE2_SPTR8) "^(\\S+)(?:\\s+(\\S)\\S*\\s+\\S+(\\s+\\S+)(\\s+\\S+))?",
PCRE2_ZERO_TERMINATED,
PCRE2_DOLLAR_ENDONLY | PCRE2_DOTALL | PCRE2_CASELESS, &errcode, &erroff, NULL);
if (!c->parse_re || !c->fallback_re)
goto fail2;
cookie_cache_init(&c->cookie_cache);
if (udp_listener_init(&c->udp_listener, ep, control_udp_incoming, &c->obj))
goto fail2;
return c;
fail2:
obj_put(c);
return NULL;
}
|