diff options
author | 2019-03-27 12:46:50 +0200 | |
---|---|---|
committer | 2019-03-29 14:41:25 +0200 | |
commit | 85f030c9ddcd1f3266d4eb6b51a9e89e1a4f5c26 (patch) | |
tree | 3110001a2cc39e3e11d90feaddbef3acd1d61c16 /src/daemon/net.c | |
parent | e2ee55bba2f55624e23dbcaeb3613dd03e60692d (diff) | |
download | usurpation-85f030c9ddcd1f3266d4eb6b51a9e89e1a4f5c26.tar.gz usurpation-85f030c9ddcd1f3266d4eb6b51a9e89e1a4f5c26.tar.bz2 usurpation-85f030c9ddcd1f3266d4eb6b51a9e89e1a4f5c26.zip |
daemon: add networking code.
Port networking code from previous project.
This commit is part of ticket 3.
Signed-off-by: Paulius Ratkevičius <pauliuz95@gmail.com>
Diffstat (limited to 'src/daemon/net.c')
-rw-r--r-- | src/daemon/net.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/src/daemon/net.c b/src/daemon/net.c new file mode 100644 index 0000000..225d6d9 --- /dev/null +++ b/src/daemon/net.c @@ -0,0 +1,229 @@ +/* + * Usurpation – newtwork logic + * + * Copyright (C) 2019 Gediminas Jakutis + * Copyright (C) 2019 Paulius Ratkevičius + * + * This program 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; version 2.1 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/udp.h> +#include <poll.h> +#include <unistd.h> +#include <pthread.h> +#include <time.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <limits.h> +#include "net.h" + +struct netstate { + struct timespec lastreply; + int nd; + int sock; + unsigned short int port; + pthread_t listner; + char *data; + size_t bufsize; + unsigned int available; + int status; + pthread_mutex_t datamutex; +}; + +static pthread_mutex_t initmutex = PTHREAD_MUTEX_INITIALIZER; +static struct netstate *state; +static ssize_t count; +static size_t available_count; + +static void *dolisten(void * state); +static int getpacket(char *data, size_t buffsize, ssize_t *recvbufsize, int sock, struct sockaddr_in *sender); + +int net_init(const unsigned short int port) +{ + struct sockaddr_in bindaddress = {0}; + int ret; + ssize_t i; + int sizechanged = 0; + + pthread_mutex_lock(&initmutex); + if (available_count) { + for (i = 0; !state[i].available; ++i) { + if (i == count) { + ret = ERROR; + goto out; + } + } + } else { + if (++count == INT_MAX) { + ret = ERROR; + goto out; + } + + i = count - 1; + state = realloc(state, sizeof(struct netstate) * count); + sizechanged = 1; + } + state[i].available = 0; + bindaddress.sin_family = AF_INET; + bindaddress.sin_port = htons(port); + state[i].sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (bind(state[i].sock, (struct sockaddr*) &bindaddress, sizeof(bindaddress))) { + ret = ERROR; + goto out; + } + + clock_gettime(CLOCK_MONOTONIC_RAW, &state[i].lastreply); + state[i].lastreply.tv_sec -= 5; + state[i].nd = i + 1; + state[i].port = port; + state[i].data = malloc(dgsize); + memset(state[i].data, 0, dgsize); + state[i].bufsize = dgsize; + state[i].status = NONEWDATA; + pthread_mutex_init(&state[i].datamutex, NULL); + + if (pthread_create(&state[i].listner, NULL, dolisten, state + i)) { + ret = ERROR; + goto out; + } + + ret = count; +out: + if (ret == ERROR && sizechanged) { + state = realloc(state, sizeof(struct netstate) * --count); + } + + pthread_mutex_unlock(&initmutex); + return ret; +} + +int net_close(int nd) +{ + int ret; + + pthread_mutex_lock(&initmutex); + if (nd > count || nd < 1 || state[nd - 1].available) { + ret = ERROR; + } else { + --nd; + pthread_cancel(state[nd].listner); + pthread_join(state[nd].listner, NULL); + close(state[nd].sock); + free(state[nd].data); + state[nd].available = 1; + ret = A_OK; + } + + pthread_mutex_unlock(&initmutex); + return ret; +} + +int net_getlastdata(int nd, char * const data) +{ + int ret; + + if (nd > count || nd < 1 || state[--nd].available) { + ret = ERROR; + } else if (!(ret = state[nd].status)) { + memcpy(data, state[nd].data, dgsize); + } + + return ret; +} + +static void *dolisten(void * state) +{ + + static const char servermagic[] = "I love coffee!"; + static const char clientmagic[] = "I love tea!"; + struct timespec now; + struct timespec wait = {0, 10 * 1000 * 1000}; /* 10 ms */ + struct sockaddr_in clientaddr; + struct netstate *st; + ssize_t recvbufsize; + int cancelstate; + + st = state; + + do { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancelstate); + pthread_mutex_lock(&st->datamutex); + clock_gettime(CLOCK_MONOTONIC_RAW, &now); + st->status = getpacket(st->data, st->bufsize, &recvbufsize, st->sock, &clientaddr); + + if (!st->status) { + st->lastreply = now; + if (st->data[0] == 'I' && !(strcmp(st->data, servermagic))) { + sendto(st->sock, clientmagic, sizeof(clientmagic), + MSG_DONTWAIT, (struct sockaddr *) &clientaddr, sizeof(clientaddr)); + st->status = NONEWDATA; /* consume packet and lie about it */ + fprintf(stderr, "Sending DATA, timestap: %li \n", st->lastreply.tv_sec); + } + } + + /* no packets in five seconds */ + if ((now.tv_sec - st->lastreply.tv_sec) >= 5) { + st->status = DEAD; + } else if (st->data[0] != 'I') { + /* we don't actually want to have NONEWDATA set from this loop, + * barring the one exeption that is after arrival of the beacon + * packet, as it'd keep the value at NONEWDATA almost constantly + * due to polling being generally much more frequent than the + * actual packet rate. + */ + st->status = A_OK; + } + + pthread_mutex_unlock(&st->datamutex); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &cancelstate); + pthread_testcancel(); + + nanosleep(&wait, NULL); + + } while (1); + + return NULL; +} + +static int getpacket(char *data, size_t buffsize, ssize_t *recvbufsize, int sock, struct sockaddr_in *sender) +{ + static struct pollfd pfd = {0}; + int ret; + socklen_t sender_len = sizeof(*sender); + + if (!pfd.fd) { + pfd.fd = sock; + pfd.events = POLLIN; + pfd.revents = 0; + } + + ret = poll(&pfd, 1, 0); + if (ret < 0) { + /* NOOP */ + } else if (!ret) { + ret = NONEWDATA; + } else if (pfd.revents & POLLIN) { + *recvbufsize = recvfrom(sock, data, buffsize, MSG_DONTWAIT, (struct sockaddr *) sender, &sender_len); + ret = A_OK; + } else { + ret = NONEWDATA; + } + + return ret; +} |