#include #include #include #include #include #include #include #include #include #include #include #include #include "libglouglou.h" /* Server - private */ struct gg_user *user_add(struct gg_server *, struct sockaddr_in *); struct gg_user *user_find(struct gg_server *, struct sockaddr_in *); void cb_srv_receive(evutil_socket_t, short, void *); void cb_cli_receive(evutil_socket_t, short, void *); /* * Server */ /* * start a server */ struct gg_server * gg_server_start(struct event_base *ev_base, char *ip, int port, int (*handle_conn)(struct gg_server *, struct gg_user *), int (*handle_packet)(struct gg_server *, struct gg_user *, struct gg_packet *)) { struct gg_server *srv; struct sockaddr_in sock_addr; struct event *ev; int s; int sock_on = 1; srv = xcalloc(1, sizeof(struct gg_server)); srv->ip = ip; srv->port = port; srv->handle_conn = handle_conn; srv->handle_packet = handle_packet; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) goto err; srv->sock = s; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &sock_on, sizeof(sock_on)); fd_nonblock(s); bzero(&sock_addr, sizeof(sock_addr)); sock_addr.sin_family = AF_INET; sock_addr.sin_addr.s_addr = inet_addr(ip); sock_addr.sin_port = htons(port); if (bind(s, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) goto err; ev = event_new(ev_base, s, EV_READ|EV_PERSIST, cb_srv_receive, srv); event_add(ev, NULL); srv->ev = ev; return srv; err: printf("libglouglou: gg_server_start: Error ! %s", strerror(errno)); gg_server_stop(srv); return NULL; } int gg_server_send(struct gg_server *s, int client_id, struct gg_packet *p) { return -1; } void gg_server_stop(struct gg_server *srv) { if (srv->ev) event_del(srv->ev); if (srv->sock) close(srv->sock); free(srv); } /* * Server - private */ struct gg_user * user_find(struct gg_server *srv, struct sockaddr_in *remote) { struct gg_user *usr; struct sockaddr_in *u; LIST_FOREACH(usr, &srv->user_list, entry) { u = &usr->addr; if (u->sin_addr.s_addr == remote->sin_addr.s_addr && u->sin_port == remote->sin_port) return usr; } return NULL; } struct gg_user * user_add(struct gg_server *srv, struct sockaddr_in *remote) { struct gg_user *usr; usr = xcalloc(1, sizeof(struct gg_user)); usr->id = srv->user_id_count; srv->user_id_count++; addrcpy(&usr->addr, remote); LIST_INSERT_HEAD(&srv->user_list, usr, entry); return usr; } struct gg_packet * pkt_decode(char **buf, int *buf_len) { char *p; int len; p = *buf; len = *buf_len; // XXX IN PROGRESS *buf = p; *buf_len = len; return NULL; } void cb_srv_receive(evutil_socket_t fd, short what, void *arg) { struct gg_server *srv; struct gg_user *usr; struct gg_packet *pkt; struct sockaddr_in remote; socklen_t remote_len; char buf[16384]; char *buf_p; int buf_len; int len; srv = arg; remote_len = sizeof(struct sockaddr_in); len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&remote, &remote_len); if (len < 0) { printf("libglouglou: cb_srv_receive: recvfrom failed\n"); return; } usr = user_find(srv, &remote); if (!usr) { usr = user_add(srv, &remote); if (srv->handle_conn) srv->handle_conn(srv, usr); printf("srv: New user %d !\n", usr->id); if (sendto(srv->sock, "", strlen(""), 0, (struct sockaddr *)&remote, remote_len) == -1) printf("srv: send failed: %s\n", strerror(errno)); } else { printf("srv: Incoming data from existing user !\n"); buf_p = buf; buf_len = len; while ((pkt = pkt_decode(&buf_p, &buf_len))) if (srv->handle_packet) srv->handle_packet(srv, usr, pkt); if (buf_len > 0) { /* XXX store incomplete packet for next recv */ printf("srv: incomplete packet, dropped %d bytes !\n", buf_len); } } } /* * Client */ /* * connect to a server */ struct gg_client * gg_client_connect(struct event_base *ev_base, char *ip, int port, int (*handle_conn)(struct gg_client *), int (*handle_packet)(struct gg_client *, struct gg_packet *)) { struct gg_client *cli; struct sockaddr_in sock_addr; struct event *ev; int s; int sock_on = 1; cli = xcalloc(1, sizeof(struct gg_client)); cli->ip = ip; cli->port = port; cli->handle_conn = handle_conn; cli->handle_packet = handle_packet; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) goto err; cli->sock = s; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &sock_on, sizeof(sock_on)); fd_nonblock(s); bzero(&sock_addr, sizeof(sock_addr)); sock_addr.sin_family = AF_INET; sock_addr.sin_addr.s_addr=inet_addr(ip); sock_addr.sin_port=htons(port); ev = event_new(ev_base, s, EV_READ|EV_PERSIST, cb_cli_receive, cli); event_add(ev, NULL); cli->ev = ev; if (sendto(s, "", strlen(""), 0, (struct sockaddr *)&sock_addr, sizeof(struct sockaddr_in)) == -1) printf("cli: send failed: %s\n", strerror(errno)); return cli; err: printf("libglouglou: gg_client_connect: Error ! %s", strerror(errno)); gg_client_disconnect(cli); return NULL; } int gg_client_send(struct gg_client *c, struct gg_packet *p) { return -1; } void gg_client_disconnect(struct gg_client *cli) { if (cli->ev) event_del(cli->ev); if (cli->sock) close(cli->sock); free(cli); } /* * Client - private */ void cb_cli_receive(evutil_socket_t fd, short what, void *arg) { struct gg_client *cli; struct gg_packet *pkt; struct sockaddr_in remote; socklen_t remote_len; char buf[16384]; char *buf_p; int buf_len; int len; cli = arg; len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&remote, &remote_len); if (len < 0) { printf("libglouglou: cb_cli_receive: recvfrom failed\n"); return; } switch (cli->status) { case GG_CLIENT_STATUS_DISCONNECTED: printf("cli: Connected !\n"); cli->status = GG_CLIENT_STATUS_CONNECTED; if (cli->handle_conn) cli->handle_conn(cli); break; case GG_CLIENT_STATUS_CONNECTED: printf("cli: Incoming data !\n"); buf_p = buf; buf_len = len; while ((pkt = pkt_decode(&buf_p, &buf_len))) if (cli->handle_packet) cli->handle_packet(cli, pkt); if (buf_len > 0) { /* XXX store incomplete packet for next recv */ printf("cli: incomplete packet, dropped %d bytes !\n", buf_len); } break; } } /* * Utils */ void * xmalloc(size_t size) { void *data; data = malloc(size); if (!data) err(1, "could not malloc %d", (int)size); return data; } void * xcalloc(size_t nmemb, size_t size) { void *data; data = calloc(nmemb, size); if (!data) err(1, "could not calloc %d", (int)size); return data; } void fd_nonblock(int fd) { int flags = fcntl(fd, F_GETFL, 0); int rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK); if (rc == -1) printf("failed to set fd %i non-blocking", fd); } void addrcpy(struct sockaddr_in *dst, struct sockaddr_in *src) { dst->sin_addr.s_addr = src->sin_addr.s_addr; dst->sin_port = src->sin_port; dst->sin_family = src->sin_family; }