/* * Hot Beverage Companion – desktop app network module * * Copyright (C) 2017 Gediminas Jakutis * * This library 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 library; 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 "net.h" struct netstate_internal { int (*getlastdata)(char * const data, struct netstate * const state); unsigned int handle; struct timespec lastreply; int sock; }; static int dolisten(char * const data, struct netstate_internal * const state); static int getpacket(char *data, size_t buffsize, int sock, struct sockaddr_in *sender); static int getlastdata_internal(char * const data, struct netstate * const state); struct netstate *net_init(const unsigned int port) { struct netstate_internal *ret; struct sockaddr_in bindaddress = {0}; ret = malloc(sizeof(struct netstate_internal)); bindaddress.sin_family = AF_INET; bindaddress.sin_port = htons(port); ret->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (bind(ret->sock, (struct sockaddr*) &bindaddress, sizeof(bindaddress))) { free(ret); return NULL; } clock_gettime(CLOCK_MONOTONIC_RAW, &ret->lastreply); ret->lastreply.tv_sec -= 5; ret->handle = 0; /* currently unused */ ret->getlastdata = &getlastdata_internal; return (struct netstate *) ret; } int net_close(struct netstate **state) { close((*(struct netstate_internal **)state)->sock); free(*state); *state = NULL; return 0; } static int dolisten(char * const data, struct netstate_internal * const state) { char buff[9001] = {0}; static const char servermagic[] = "I love coffee!"; static const char clientmagic[] = "I love tea!"; struct timespec now; struct timespec wait = {0, 25 * 1000 * 1000}; struct sockaddr_in clientaddr; size_t i = 10; int ret; do { clock_gettime(CLOCK_MONOTONIC_RAW, &now); ret = getpacket(buff, sizeof(buff), state->sock, &clientaddr); switch (ret) { case NET_OK: state->lastreply = now; if (buff[0] == 'I') { if (!(strcmp(buff, servermagic))) { sendto(state->sock, clientmagic, sizeof(clientmagic), MSG_DONTWAIT, (struct sockaddr *) &clientaddr, sizeof(clientaddr)); } ret = NET_NONEWDATA; /* consume packet and lie about it */ continue; } else { memcpy(data, buff, 8ul); } break; default: ; /* noop */ } if (!ret) { nanosleep(&wait, NULL); } } while (--i && ret != 0); /* no packets in five seconds */ if ((now.tv_sec - state->lastreply.tv_sec) >= 5) { ret = NET_DEAD; } return ret; } static int getpacket(char *data, size_t buffsize, 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 = NET_NONEWDATA; } else if (pfd.revents & POLLIN) { recvfrom(sock, data, buffsize, MSG_DONTWAIT, (struct sockaddr *) sender, &sender_len); ret = NET_OK; } else { ret = NET_NONEWDATA; } return ret; } static int getlastdata_internal(char * const data, struct netstate * const state) { return dolisten(data, (struct netstate_internal *) state); }