/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "settings.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(MTU); memset(state[i].data, 0, MTU); state[i].bufsize = MTU; 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)) { if (!data) { ret = ERROR; goto out; } else if (!(*data)) { *data = malloc(MTU); } memset(*data, 0, MTU); memcpy(*data, state[nd].data, MTU); } out: 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; int oldstatus = DEAD; char *ipstring; 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)); setting_verbose() ? fprintf(stderr, "Sending DATA, timestap: %li \n", st->lastreply.tv_sec) : 0; } } /* no packets in five seconds */ if ((now.tv_sec - st->lastreply.tv_sec) >= 5) { st->status = DEAD; } else { st->status = A_OK; } if (oldstatus != st->status) { oldstatus = st->status; if(st->status == DEAD) { /* this timestamp is arbitraty */ setting_verbose() ? fprintf(stderr, "Connection with the client has been lost. Last reply since: %li \n", st->lastreply.tv_sec) : 0; } else { ipstring = inet_ntoa(clientaddr.sin_addr); setting_verbose() ? fprintf(stderr, "Successful incoming connection from %s\n", ipstring) : 0; } } 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; }