From 8481d09ef47a030216bed1bf6f1111104c61bc2f Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 10 Apr 2007 01:30:47 +0000 Subject: [PATCH] socket2me git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@4906 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- scripts/socket/socket2me/Makefile | 4 + scripts/socket/socket2me/README | 20 ++ scripts/socket/socket2me/socket2me.c | 440 +++++++++++++++++++++++++++ 3 files changed, 464 insertions(+) create mode 100644 scripts/socket/socket2me/Makefile create mode 100644 scripts/socket/socket2me/README create mode 100644 scripts/socket/socket2me/socket2me.c diff --git a/scripts/socket/socket2me/Makefile b/scripts/socket/socket2me/Makefile new file mode 100644 index 0000000000..235916d8ab --- /dev/null +++ b/scripts/socket/socket2me/Makefile @@ -0,0 +1,4 @@ +socket2me: socket2me.c + $(CC) -Wall socket2me.c -o socket2me -l spandsp +clean: + rm socket2me diff --git a/scripts/socket/socket2me/README b/scripts/socket/socket2me/README new file mode 100644 index 0000000000..cdc74f7bb6 --- /dev/null +++ b/scripts/socket/socket2me/README @@ -0,0 +1,20 @@ +EXAMPLE OF EVENT SOCKET EXTERNAL APP: REQUIRES mod_event_socket + +Set this or something similar in your dialplan + + + + + + + + + + + +and in a separate process run +#./socket2me 127.0.0.1 127.0.0.1 8084 + + + + diff --git a/scripts/socket/socket2me/socket2me.c b/scripts/socket/socket2me/socket2me.c new file mode 100644 index 0000000000..013c982ee9 --- /dev/null +++ b/scripts/socket/socket2me/socket2me.c @@ -0,0 +1,440 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * * Neither the name of [original copyright holder] nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Copyright (C) 2007, Anthony Minessale II + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOCKET2ME_DEBUG 0 +#define MAXPENDING 10000 +#define RCVBUFSIZE 4198 +#define PORT_MIN 9000 +#define PORT_MAX 10000 + +static int use_port = PORT_MIN; + +static void phase_b_handler(t30_state_t *s, void *user_data, int result) +{ + int session; + session = (intptr_t) user_data; + + printf("Phase B handler on session %d - (0x%X) %s\n", session, result, t30_frametype(result)); +} + +static void phase_d_handler(t30_state_t *s, void *user_data, int result) +{ + int session; + char ident[21]; + t30_stats_t t; + + session = (intptr_t) user_data; + + printf("Phase D handler on session %d - (0x%X) %s\n", session, result, t30_frametype(result)); + t30_get_transfer_statistics(s, &t); + printf( "Phase D: bit rate %d\n", t.bit_rate); + printf( "Phase D: ECM %s\n", (t.error_correcting_mode) ? "on" : "off"); + printf( "Phase D: pages transferred %d\n", t.pages_transferred); + printf( "Phase D: image size %d x %d\n", t.width, t.length); + printf( "Phase D: image resolution %d x %d\n", t.x_resolution, t.y_resolution); + printf( "Phase D: bad rows %d\n", t.bad_rows); + printf( "Phase D: longest bad row run %d\n", t.longest_bad_row_run); + printf( "Phase D: compression type %d\n", t.encoding); + printf( "Phase D: image size %d\n", t.image_size); + t30_get_local_ident(s, ident); + printf( "Phase D: local ident '%s'\n", ident); + t30_get_far_ident(s, ident); + printf( "Phase D: remote ident '%s'\n", ident); +} + +static void phase_e_handler(t30_state_t *s, void *user_data, int result) +{ + int session; + t30_stats_t t; + const char *u; + char ident[21]; + + session = (intptr_t) user_data; + printf("Phase E handler on session %d - (%d) %s\n", session, result, t30_completion_code_to_str(result)); + t30_get_transfer_statistics(s, &t); + printf( "Phase E: bit rate %d\n", t.bit_rate); + printf( "Phase E: ECM %s\n", (t.error_correcting_mode) ? "on" : "off"); + printf( "Phase E: pages transferred %d\n", t.pages_transferred); + printf( "Phase E: image size %d x %d\n", t.width, t.length); + printf( "Phase E: image resolution %d x %d\n", t.x_resolution, t.y_resolution); + printf( "Phase E: bad rows %d\n", t.bad_rows); + printf( "Phase E: longest bad row run %d\n", t.longest_bad_row_run); + printf( "Phase E: coding method %s\n", t4_encoding_to_str(t.encoding)); + printf( "Phase E: image size %d bytes\n", t.image_size); + t30_get_local_ident(s, ident); + printf( "Phase E: local ident '%s'\n", ident); + t30_get_far_ident(s, ident); + printf( "Phase E: remote ident '%s'\n", ident); + if ((u = t30_get_far_country(s))) + printf( "Phase E: Remote was made in '%s'\n", u); + if ((u = t30_get_far_vendor(s))) + printf( "Phase E: Remote was made by '%s'\n", u); + if ((u = t30_get_far_model(s))) + printf( "Phase E: Remote is model '%s'\n", u); +} + +static int document_handler(t30_state_t *s, void *user_data, int event) +{ + int session; + + session = (intptr_t) user_data; + printf("Document handler on session %d - event %d\n", session, event); + return FALSE; +} + +void die(char *error_str) +{ + perror(error_str); + exit(1); +} + + +static int cheezy_get_var(char *data, char *name, char *buf, size_t buflen) +{ + char *p; + + if ((p = strstr(data, name))) { + char *v, *e; + + if ((v = strchr(p, ':'))) { + v++; + while(v && *v == ' ') { + v++; + } + if (v) { + if (!(e = strchr(v, '\r'))) { + e = strchr(v, '\n'); + } + } + + if (v && e) { + int cplen; + int len = e - v; + + if (len > buflen - 1) { + cplen = buflen -1; + } else { + cplen = len; + } + + strncpy(buf, v, cplen); + *(buf+cplen) = '\0'; + return 1; + } + + } + } + return 0; +} + +void client_run(int client_socket, char *local_ip, int local_port, char *remote_ip, int remote_port) +{ + char sendbuf[RCVBUFSIZE], recvbuf[RCVBUFSIZE], infobuf[RCVBUFSIZE]; + struct sockaddr_in addr = {0}, sendaddr = {0}; + int read_bytes; + int usock; + int reuse_addr = 1; + fax_state_t fax; + char tmp[512], fn[512], *file_name = "/tmp/test.tiff"; + int send_fax = FALSE; + + snprintf(sendbuf, sizeof(sendbuf), "connect\n\n"); + send(client_socket, sendbuf, strlen(sendbuf), 0); + + if ((read_bytes = recv(client_socket, infobuf, sizeof(infobuf), 0)) < 0) { + die("recv() failed"); + } + +#if SOCKET2ME_DEBUG + printf("READ [%s]\n", infobuf); +#endif + snprintf(sendbuf, sizeof(sendbuf), "sendmsg\n" + "call-command: unicast\n" + "local_ip: %s\n" + "local_port: %d\n" + "remote_ip: %s\n" + "remote_port: %d\n" + "transport: udp\n\n", + local_ip, local_port, + remote_ip, remote_port + ); + + if (cheezy_get_var(infobuf, "variable_fax_file_name", fn, sizeof(fn))) { + file_name = fn; + } + + if (cheezy_get_var(infobuf, "variable_fax_mode", tmp, sizeof(tmp))) { + if (!strcasecmp(tmp, "send")) { + send_fax = TRUE; + } + } +#if SOCKET2ME_DEBUG + printf("SEND: [%s]\n", sendbuf); +#endif + send(client_socket, sendbuf, strlen(sendbuf), 0); + + memset(recvbuf, 0, sizeof(recvbuf)); + if ((read_bytes = recv(client_socket, recvbuf, sizeof(recvbuf), 0)) < 0) { + die("recv() failed"); + } +#if SOCKET2ME_DEBUG + printf("READ [%s]\n", recvbuf); +#endif + + if ((usock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + die("socket() failed"); + } + + setsockopt(usock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + sendaddr.sin_addr.s_addr = inet_addr(remote_ip); + addr.sin_port = htons(remote_port); + + sendaddr.sin_family = AF_INET; + sendaddr.sin_addr.s_addr = inet_addr(local_ip); + sendaddr.sin_port = htons(local_port); + + if (bind(usock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + die("bind() failed"); + } + + printf("%s Fax filename: [%s] from %s:%d -> %s:%d\n", send_fax ? "Sending" : "Receiving", file_name, local_ip, local_port, remote_ip, remote_port); + + fax_init(&fax, send_fax); + t30_set_local_ident(&fax.t30_state, "Socket 2 ME"); + t30_set_header_info(&fax.t30_state, "Socket 2 ME"); + if (send_fax) { + t30_set_tx_file(&fax.t30_state, file_name, -1, -1); + } else { + t30_set_rx_file(&fax.t30_state, file_name, -1); + } + t30_set_phase_b_handler(&fax.t30_state, phase_b_handler, NULL); + t30_set_phase_d_handler(&fax.t30_state, phase_d_handler, NULL); + t30_set_phase_e_handler(&fax.t30_state, phase_e_handler, NULL); + t30_set_document_handler(&fax.t30_state, document_handler, NULL); + + t30_set_ecm_capability(&fax.t30_state, TRUE); + t30_set_supported_compressions(&fax.t30_state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION); + + t30_set_supported_image_sizes(&fax.t30_state, T30_SUPPORT_US_LETTER_LENGTH | T30_SUPPORT_US_LEGAL_LENGTH | T30_SUPPORT_UNLIMITED_LENGTH + | T30_SUPPORT_215MM_WIDTH | T30_SUPPORT_255MM_WIDTH | T30_SUPPORT_303MM_WIDTH); + t30_set_supported_resolutions(&fax.t30_state, T30_SUPPORT_STANDARD_RESOLUTION | T30_SUPPORT_FINE_RESOLUTION | T30_SUPPORT_SUPERFINE_RESOLUTION + | T30_SUPPORT_R8_RESOLUTION | T30_SUPPORT_R16_RESOLUTION); + + for (;;) { + struct sockaddr_in local_addr = {0}; + int cliAddrLen = sizeof(local_addr); + short audioBuffer[1024], outbuf[1024]; + int tx, bigger; + fd_set ready; + + FD_ZERO(&ready); + + FD_SET(usock, &ready); + FD_SET(client_socket, &ready); + + bigger = usock > client_socket ? usock : client_socket; + select(++bigger, &ready, NULL, NULL, NULL); + + if (FD_ISSET(client_socket, &ready)) { + memset(recvbuf, 0, sizeof(recvbuf)); + if ((read_bytes = recv(client_socket, recvbuf, sizeof(recvbuf), 0)) < 0) { + die("recv() failed"); + } + + if (read_bytes == 0) { + break; + } +#if SOCKET2ME_DEBUG + printf("READ [%s]\n", recvbuf); +#endif + } + + if (!FD_ISSET(usock, &ready)) { + continue; + } + + if ((read_bytes = recvfrom(usock, audioBuffer, sizeof(audioBuffer), 0, (struct sockaddr *) &local_addr, &cliAddrLen)) < 0) { + die("recvfrom() failed"); + } + + fax_rx(&fax, (int16_t *)audioBuffer, read_bytes / 2); +#if SOCKET2ME_DEBUG + printf("Handling client %s:%d %d bytes\n", inet_ntoa(local_addr.sin_addr), ntohs(local_addr.sin_port), read_bytes); +#endif + if ((tx = fax_tx(&fax, (int16_t *) &outbuf, read_bytes / 2)) < 0) { + printf("Fax Error\n"); + break; + } else if (!tx) { + continue; + } + + cliAddrLen = sizeof(sendaddr); + if (sendto(usock, outbuf, tx * 2, 0, (struct sockaddr *) &sendaddr, sizeof(sendaddr)) != tx * 2) { + die("sendto() sent a different number of bytes than expected"); + } + } + + close(client_socket); + close(usock); + + t30_terminate(&fax.t30_state); + fax_release(&fax); + + printf("Done\n"); + snprintf(sendbuf, sizeof(sendbuf), "hangup\n\n"); + send(client_socket, sendbuf, strlen(sendbuf), 0); + +} + +int client_accept(int servSock) +{ + int client_sock; + struct sockaddr_in echoClntAddr; + unsigned int clntLen; + + clntLen = sizeof(echoClntAddr); + + if ((client_sock = accept(servSock, (struct sockaddr *) &echoClntAddr, &clntLen)) < 0) { + die("accept() failed"); + } + + printf("Client Connect: [%s]\n", inet_ntoa(echoClntAddr.sin_addr)); + + return client_sock; +} + +int main(int argc, char *argv[]) +{ + int servSock, client_sock; + int port = 8084; + struct sockaddr_in addr; + pid_t pid; + unsigned int process_count = 0; + int reuse_addr = 1; + char *local_ip = NULL; + char *remote_ip = NULL; + char *signal_port_name = NULL; + + if (argc > 0) { + local_ip = argv[1]; + } + if (argc > 1) { + remote_ip = argv[2]; + } + if (argc > 2) { + signal_port_name = argv[3]; + } + + if (!local_ip) { + local_ip = "127.0.0.1"; + } + + if (!remote_ip) { + remote_ip = "127.0.0.1"; + } + + if (signal_port_name) { + port = atoi(signal_port_name); + } + + if ((servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + die("Socket Error!\n"); + } + setsockopt(servSock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(port); + + if (bind(servSock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + die("Bind Error!\n"); + return -1; + } + + if (listen(servSock, MAXPENDING) < 0) { + die("Listen error\n"); + return -1; + } + for (;;) { + int local_port = use_port++; + int remote_port = use_port++; + + if (use_port++ >= PORT_MAX) { + use_port = PORT_MIN; + } + + client_sock = client_accept(servSock); + + if ((pid = fork()) < 0) { + die("fork() failed"); + } else if (pid == 0) { + + close(servSock); + + client_run(client_sock, local_ip, local_port, remote_ip, remote_port); + exit(0); + } + +#if SOCKET2ME_DEBUG + printf("with child process: %d\n", (int) pid); +#endif + close(client_sock); + process_count++; + + while (process_count) { + pid = waitpid((pid_t) -1, NULL, WNOHANG); + if (pid < 0) { + die("waitpid() failed"); + } else if (pid == 0) { + break; + } else { + process_count--; + } + } + } +}