summaryrefslogtreecommitdiffstats
path: root/src/server/net.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/net.c')
-rw-r--r--src/server/net.c149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/server/net.c b/src/server/net.c
new file mode 100644
index 0000000..cecf10c
--- /dev/null
+++ b/src/server/net.c
@@ -0,0 +1,149 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <poll.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#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);
+}