From 727a33b6ebbcd79fb965fdd3eb44e4f94fa2a2b4 Mon Sep 17 00:00:00 2001 From: Pierre Pronchery Date: Sat, 22 Mar 2014 19:00:53 +0900 Subject: [PATCH] Getting closer to functional UDP calls --- Makefile | 1 + src/transport/Makefile | 12 +- src/transport/common.c | 62 +++++++++ src/transport/project.conf | 16 ++- src/transport/tcp.c | 48 +------ src/transport/udp.c | 251 ++++++++++++++++++++++++++----------- 6 files changed, 264 insertions(+), 126 deletions(-) create mode 100644 src/transport/common.c diff --git a/Makefile b/Makefile index 27f20e9..9d59e54 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,7 @@ dist: $(PACKAGE)-$(VERSION)/src/transport/udp4.c \ $(PACKAGE)-$(VERSION)/src/transport/udp6.c \ $(PACKAGE)-$(VERSION)/src/transport/Makefile \ + $(PACKAGE)-$(VERSION)/src/transport/common.c \ $(PACKAGE)-$(VERSION)/src/transport/project.conf \ $(PACKAGE)-$(VERSION)/tests/appmessage.c \ $(PACKAGE)-$(VERSION)/tests/transport.c \ diff --git a/src/transport/Makefile b/src/transport/Makefile index db7da03..9aabe4f 100644 --- a/src/transport/Makefile +++ b/src/transport/Makefile @@ -68,25 +68,25 @@ udp6_LDFLAGS = $(LDFLAGSF) $(LDFLAGS) udp6.so: $(udp6_OBJS) $(CCSHARED) -o udp6.so $(udp6_OBJS) $(udp6_LDFLAGS) -tcp.o: tcp.c +tcp.o: tcp.c common.c $(CC) $(tcp_CFLAGS) -c tcp.c -tcp4.o: tcp4.c tcp.c +tcp4.o: tcp4.c tcp.c common.c $(CC) $(tcp4_CFLAGS) -c tcp4.c -tcp6.o: tcp6.c tcp.c +tcp6.o: tcp6.c tcp.c common.c $(CC) $(tcp6_CFLAGS) -c tcp6.c template.o: template.c $(CC) $(template_CFLAGS) -c template.c -udp.o: udp.c +udp.o: udp.c common.c $(CC) $(udp_CFLAGS) -c udp.c -udp4.o: udp4.c udp.c +udp4.o: udp4.c udp.c common.c $(CC) $(udp4_CFLAGS) -c udp4.c -udp6.o: udp6.c udp.c +udp6.o: udp6.c udp.c common.c $(CC) $(udp6_CFLAGS) -c udp6.c clean: diff --git a/src/transport/common.c b/src/transport/common.c new file mode 100644 index 0000000..cc2bd00 --- /dev/null +++ b/src/transport/common.c @@ -0,0 +1,62 @@ +/* $Id$ */ +/* Copyright (c) 2014 Pierre Pronchery */ +/* This file is part of DeforaOS System libApp */ +/* This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + + + +/* init_address */ +static int _init_address(Class * instance, char const * name, int domain) +{ + char sep = ':'; + char * p; + char * q; + char * r; + long l; + struct addrinfo hints; + int res = 0; + + /* guess the port separator */ + if((q = strchr(name, ':')) != NULL && strchr(++q, ':') != NULL) + sep = '.'; + /* obtain the name */ + if((p = strdup(name)) == NULL) + return -error_set_code(1, "%s", strerror(errno)); + /* obtain the port number */ + if((q = strrchr(p, sep)) == NULL) + l = -error_set_code(1, "%s", strerror(EINVAL)); + else + { + *(q++) = '\0'; + l = strtol(q, &r, 10); + if(q[0] == '\0' || *r != '\0') + l = -error_set_code(1, "%s", strerror(EINVAL)); + } + /* FIXME perform this asynchronously */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = domain; + hints.ai_socktype = SOCK_STREAM; + if(l >= 0) + res = getaddrinfo(p, q, &hints, &instance->ai); + free(p); + /* check for errors */ + if(res != 0) + { + error_set_code(1, "%s", gai_strerror(res)); + if(instance->ai != NULL) + freeaddrinfo(instance->ai); + instance->ai = NULL; + return -1; + } + return (instance->ai != NULL) ? 0 : -1; +} diff --git a/src/transport/project.conf b/src/transport/project.conf index 39284e1..bfe3715 100644 --- a/src/transport/project.conf +++ b/src/transport/project.conf @@ -4,20 +4,23 @@ cppflags= cflags_force=-W -fPIC `pkg-config --cflags libSystem` cflags=-Wall -g -O2 -pedantic ldflags_force=`pkg-config --cflags libSystem` -dist=Makefile +dist=Makefile,common.c [tcp] type=plugin sources=tcp.c install=$(LIBDIR)/App/transport +[tcp.c] +depends=common.c + [tcp4] type=plugin sources=tcp4.c install=$(LIBDIR)/App/transport [tcp4.c] -depends=tcp.c +depends=tcp.c,common.c [tcp6] type=plugin @@ -25,7 +28,7 @@ sources=tcp6.c install=$(LIBDIR)/App/transport [tcp6.c] -depends=tcp.c +depends=tcp.c,common.c [template] type=plugin @@ -36,13 +39,16 @@ type=plugin sources=udp.c install=$(LIBDIR)/App/transport +[udp.c] +depends=common.c + [udp4] type=plugin sources=udp4.c install=$(LIBDIR)/App/transport [udp4.c] -depends=udp.c +depends=udp.c,common.c [udp6] type=plugin @@ -50,4 +56,4 @@ sources=udp6.c install=$(LIBDIR)/App/transport [udp6.c] -depends=udp.c +depends=udp.c,common.c diff --git a/src/transport/tcp.c b/src/transport/tcp.c index 840d4c7..29fecf5 100644 --- a/src/transport/tcp.c +++ b/src/transport/tcp.c @@ -98,6 +98,9 @@ struct _AppTransportPlugin /* constants */ #define INC 1024 +#define Class TCP +#include "common.c" + /* protected */ /* prototypes */ @@ -191,51 +194,6 @@ static TCP * _tcp_init(AppTransportPluginHelper * helper, return tcp; } -static int _init_address(TCP * tcp, char const * name, int domain) -{ - char sep = ':'; - char * p; - char * q; - char * r; - long l; - struct addrinfo hints; - int res = 0; - - /* guess the port separator */ - if((q = strchr(name, ':')) != NULL && strchr(++q, ':') != NULL) - sep = '.'; - /* obtain the name */ - if((p = strdup(name)) == NULL) - return -error_set_code(1, "%s", strerror(errno)); - /* obtain the port number */ - if((q = strrchr(p, sep)) == NULL) - l = -error_set_code(1, "%s", strerror(EINVAL)); - else - { - *(q++) = '\0'; - l = strtol(q, &r, 10); - if(q[0] == '\0' || *r != '\0') - l = -error_set_code(1, "%s", strerror(EINVAL)); - } - /* FIXME perform this asynchronously */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = domain; - hints.ai_socktype = SOCK_STREAM; - if(l >= 0) - res = getaddrinfo(p, q, &hints, &tcp->ai); - free(p); - /* check for errors */ - if(res != 0) - { - error_set_code(1, "%s", gai_strerror(res)); - if(tcp->ai != NULL) - freeaddrinfo(tcp->ai); - tcp->ai = NULL; - return -1; - } - return (tcp->ai != NULL) ? 0 : -1; -} - static int _init_client(TCP * tcp, char const * name) { struct addrinfo * aip; diff --git a/src/transport/udp.c b/src/transport/udp.c index 437360d..225faab 100644 --- a/src/transport/udp.c +++ b/src/transport/udp.c @@ -33,6 +33,7 @@ # include #endif #include +#include "App/appmessage.h" #include "App/apptransport.h" /* portability */ @@ -51,6 +52,14 @@ /* types */ typedef struct _AppTransportPlugin UDP; +typedef struct _UDPClient +{ + AppTransportClient * client; + + struct sockaddr * sa; + socklen_t sa_len; +} UDPClient; + typedef struct _UDPMessage { char * buffer; @@ -66,11 +75,24 @@ struct _AppTransportPlugin struct addrinfo * ai; struct addrinfo * aip; + union + { + struct + { + /* for servers */ + UDPClient ** clients; + size_t clients_cnt; + } server; + } u; + /* output queue */ UDPMessage * messages; size_t messages_cnt; }; +#define Class UDP +#include "common.c" + /* protected */ /* prototypes */ @@ -84,6 +106,10 @@ static int _udp_send(UDP * udp, AppMessage * message, int acknowledge); /* useful */ static int _udp_error(char const * message, int code); +/* clients */ +static int _udp_client_init(UDPClient * client, struct sockaddr * sa, + socklen_t sa_len, UDP * udp); + /* callbacks */ static int _udp_callback_read(int fd, UDP * udp); @@ -145,53 +171,9 @@ static UDP * _udp_init(AppTransportPluginHelper * helper, return udp; } -static int _init_address(UDP * udp, char const * name, int domain) -{ - char sep = ':'; - char * p; - char * q; - char * r; - long l; - struct addrinfo hints; - int res = 0; - - /* guess the port separator */ - if((q = strchr(name, ':')) != NULL && strchr(++q, ':') != NULL) - sep = '.'; - /* obtain the name */ - if((p = strdup(name)) == NULL) - return -error_set_code(1, "%s", strerror(errno)); - /* obtain the port number */ - if((q = strrchr(p, sep)) == NULL) - l = -error_set_code(1, "%s", strerror(EINVAL)); - else - { - *(q++) = '\0'; - l = strtol(q, &r, 10); - if(q[0] == '\0' || *r != '\0') - l = -error_set_code(1, "%s", strerror(EINVAL)); - } - /* FIXME perform this asynchronously */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = domain; - hints.ai_socktype = SOCK_DGRAM; - if(l >= 0) - res = getaddrinfo(p, q, &hints, &udp->ai); - free(p); - /* check for errors */ - if(res != 0) - { - error_set_code(1, "%s", gai_strerror(res)); - if(udp->ai != NULL) - freeaddrinfo(udp->ai); - udp->ai = NULL; - return -1; - } - return (udp->ai != NULL) ? 0 : -1; -} - static int _init_client(UDP * udp, char const * name) { + memset(&udp->u, 0, sizeof(udp->u)); /* obtain the remote address */ if(_init_address(udp, name, UDP_FAMILY) != 0) return -1; @@ -200,14 +182,6 @@ static int _init_client(UDP * udp, char const * name) /* create the socket */ if(_init_socket(udp, udp->aip->ai_family) != 0) continue; - /* accept incoming messages */ - if(bind(udp->fd, udp->aip->ai_addr, udp->aip->ai_addrlen) != 0) - { - _udp_error("bind", 1); - close(udp->fd); - udp->fd = -1; - continue; - } /* listen for incoming messages */ event_register_io_read(udp->helper->event, udp->fd, (EventIOFunc)_udp_callback_read, udp); @@ -224,6 +198,8 @@ static int _init_client(UDP * udp, char const * name) static int _init_server(UDP * udp, char const * name) { + udp->u.server.clients = NULL; + udp->u.server.clients_cnt = 0; /* obtain the local address */ if(_init_address(udp, name, UDP_FAMILY) != 0) return -1; @@ -232,6 +208,14 @@ static int _init_server(UDP * udp, char const * name) /* create the socket */ if(_init_socket(udp, udp->aip->ai_family) != 0) continue; + /* accept incoming messages */ + if(bind(udp->fd, udp->aip->ai_addr, udp->aip->ai_addrlen) != 0) + { + _udp_error("bind", 1); + close(udp->fd); + udp->fd = -1; + continue; + } /* listen for incoming messages */ event_register_io_read(udp->helper->event, udp->fd, (EventIOFunc)_udp_callback_read, udp); @@ -266,8 +250,18 @@ static int _init_socket(UDP * udp, int domain) /* udp_destroy */ +static void _destroy_server(UDP * udp); + static void _udp_destroy(UDP * udp) { + switch(udp->mode) + { + case ATM_CLIENT: + break; + case ATM_SERVER: + _destroy_server(udp); + break; + } if(udp->fd >= 0) close(udp->fd); free(udp->messages); @@ -276,31 +270,57 @@ static void _udp_destroy(UDP * udp) object_delete(udp); } +static void _destroy_server(UDP * udp) +{ + AppTransportPluginHelper * helper = udp->helper; + size_t i; + + for(i = 0; i < udp->u.server.clients_cnt; i++) + { + helper->client_delete(helper->transport, + udp->u.server.clients[i]->client); + free(udp->u.server.clients[i]->sa); + free(udp->u.server.clients[i]); + } +} + /* udp_send */ static int _udp_send(UDP * udp, AppMessage * message, int acknowledge) { + int ret; + Buffer * buffer; #ifdef DEBUG struct sockaddr_in * sa; #endif if(udp->mode != ATM_CLIENT) return -error_set_code(1, "%s", "Not a client"); + /* FIXME confirm the message will be consistent and readable */ + if((buffer = buffer_new(0, NULL)) == NULL) + return -1; + if(appmessage_serialize(message, buffer) != 0) + { + buffer_delete(buffer); + return -1; + } #ifdef DEBUG if(udp->aip->ai_family == AF_INET) { sa = (struct sockaddr_in *)udp->aip->ai_addr; - fprintf(stderr, "DEBUG: %s() %s (%s:%u)\n", __func__, + fprintf(stderr, "DEBUG: %s() %s (%s:%u) size=%lu\n", __func__, "sendto()", inet_ntoa(sa->sin_addr), - ntohs(sa->sin_port)); + ntohs(sa->sin_port), buffer_get_size(buffer)); } else - fprintf(stderr, "DEBUG: %s() %s %d\n", __func__, - "sendto()", udp->aip->ai_family); + fprintf(stderr, "DEBUG: %s() %s family=%d size=%lu\n", __func__, + "sendto()", udp->aip->ai_family, + buffer_get_size(buffer)); #endif - /* FIXME really implement */ - return sendto(udp->fd, NULL, 0, 0, udp->aip->ai_addr, - udp->aip->ai_addrlen); + ret = sendto(udp->fd, buffer_get_data(buffer), buffer_get_size(buffer), + 0, udp->aip->ai_addr, udp->aip->ai_addrlen); + buffer_delete(buffer); + return ret; } @@ -313,14 +333,42 @@ static int _udp_error(char const * message, int code) } +/* clients */ +/* udp_client_init */ +static int _udp_client_init(UDPClient * client, struct sockaddr * sa, + socklen_t sa_len, UDP * udp) +{ + AppTransportPluginHelper * helper = udp->helper; + + if((client->sa = malloc(sa_len)) == NULL) + return -1; + if((client->client = helper->client_new(helper->transport)) == NULL) + { + free(client->sa); + return -1; + } + memcpy(client->sa, sa, sa_len); + client->sa_len = sa_len; + return 0; +} + + /* callbacks */ /* udp_callback_read */ +static void _callback_read_client(UDP * udp, struct sockaddr * sa, + socklen_t sa_len, AppMessage * message); +static void _callback_read_server(UDP * udp, struct sockaddr * sa, + socklen_t sa_len, AppMessage * message); + static int _udp_callback_read(int fd, UDP * udp) { char buf[65536]; ssize_t ssize; struct sockaddr * sa; socklen_t sa_len = udp->aip->ai_addrlen; + size_t size; + Buffer * buffer; + AppMessage * message = NULL; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%d)\n", __func__, fd); @@ -339,18 +387,81 @@ static int _udp_callback_read(int fd, UDP * udp) udp->fd = -1; return -1; } - else if(ssize == 0) - { - /* FIXME really implement */ - } - else - { - /* FIXME really implement */ - } #ifdef DEBUG - fprintf(stderr, "DEBUG: %s() received message (%ld)\n", __func__, - ssize); + fprintf(stderr, "DEBUG: %s() ssize=%ld\n", __func__, ssize); #endif + size = ssize; + if((buffer = buffer_new(ssize, buf)) == NULL) + { + free(sa); + return 0; + } + message = appmessage_new_deserialize(buffer); + buffer_delete(buffer); + if(message == NULL) + { + /* FIXME report error */ + free(sa); + return 0; + } + switch(udp->mode) + { + case ATM_CLIENT: + _callback_read_client(udp, sa, sa_len, message); + break; + case ATM_SERVER: + _callback_read_server(udp, sa, sa_len, message); + break; + } + appmessage_delete(message); free(sa); return 0; } + +static void _callback_read_client(UDP * udp, struct sockaddr * sa, + socklen_t sa_len, AppMessage * message) +{ + /* FIXME implement */ +} + +static void _callback_read_server(UDP * udp, struct sockaddr * sa, + socklen_t sa_len, AppMessage * message) +{ + AppTransportPluginHelper * helper = udp->helper; + size_t i; + UDPClient ** p; + AppTransportClient * client; + +#ifdef DEBUG + fprintf(stderr, "DEBUG: %s()\n", __func__); +#endif + for(i = 0; i < udp->u.server.clients_cnt; i++) + if(udp->u.server.clients[i]->sa_len == sa_len + && memcmp(udp->u.server.clients[i]->sa, sa, + sa_len) == 0) + break; + if(i == udp->u.server.clients_cnt) + { + if((p = realloc(udp->u.server.clients, sizeof(*p) * (i + 1))) + == NULL) + /* FIXME report error */ + return; + udp->u.server.clients = p; + if((udp->u.server.clients[i] = malloc(sizeof(*p))) == NULL) + /* FIXME report error */ + return; + if(_udp_client_init(udp->u.server.clients[i], sa, sa_len, udp) + != 0) + { + /* FIXME report error */ + free(p[i]); + return; + } + udp->u.server.clients_cnt++; + } + client = udp->u.server.clients[i]->client; + helper->client_receive(helper->transport, client, message); +#ifdef DEBUG + fprintf(stderr, "DEBUG: %s() received message\n", __func__); +#endif +}