diff options
author | tomsmeding <tom.smeding@gmail.com> | 2017-01-19 18:30:07 +0100 |
---|---|---|
committer | tomsmeding <tom.smeding@gmail.com> | 2017-01-19 18:30:07 +0100 |
commit | fcc0658730e541477fa60995d8bd82ff87163b92 (patch) | |
tree | 7e993fd0cbc54791d3e641badf8965d0d3a2952f | |
parent | 89f613475b8da3076aff70a6cef6b41df551d567 (diff) |
Separate sock_/tcp_ like regexbattle
-rw-r--r-- | global.c | 16 | ||||
-rw-r--r-- | global.h | 10 | ||||
-rw-r--r-- | main.c | 207 | ||||
-rw-r--r-- | tcp.c | 191 | ||||
-rw-r--r-- | tcp.h | 35 |
5 files changed, 299 insertions, 160 deletions
diff --git a/global.c b/global.c new file mode 100644 index 0000000..27f522c --- /dev/null +++ b/global.c @@ -0,0 +1,16 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include "global.h" + +__attribute__((noreturn, format (printf, 1, 2))) +void throw(const char *format,...){ + char *buf; + va_list ap; + va_start(ap,format); + vasprintf(&buf,format,ap); + va_end(ap); + fprintf(stderr,"THROW: %s\n",buf); + exit(1); +} diff --git a/global.h b/global.h new file mode 100644 index 0000000..61d8cee --- /dev/null +++ b/global.h @@ -0,0 +1,10 @@ +#pragma once + +#include <stdint.h> +#include <inttypes.h> + +typedef int64_t i64; +typedef uint64_t u64; + + +void throw(const char *format,...) __attribute__((noreturn, format (printf, 1, 2))); @@ -11,23 +11,12 @@ #include <sys/socket.h> #include <sys/select.h> #include <errno.h> +#include "global.h" #include "memory.h" +#include "tcp.h" #define MAX_LISTEN (128) -typedef int64_t i64; - - -__attribute__((noreturn, format (printf, 1, 2))) -void throw(const char *format,...){ - char *buf; - va_list ap; - va_start(ap,format); - vasprintf(&buf,format,ap); - va_end(ap); - fprintf(stderr,"THROW: %s\n",buf); - exit(1); -} i64 max(i64 a,i64 b){ return a>b?a:b; @@ -148,6 +137,7 @@ i64 list_find_d(void **d,i64 len,void *value){ //Returns false iff conn was already in specified room. bool room_join(Room *room,Connection *conn){ if(LIST_FIND(&room->members,conn)!=-1)return false; + //TODO: notify other members clist_add(&room->members,conn); rlist_add(&conn->rooms,room); return true; @@ -159,9 +149,9 @@ void room_destroy(Room *room); bool room_leave(Room *room,Connection *conn){ i64 idx=LIST_FIND(&room->members,conn); if(idx==-1)return false; - //TODO: notify other members clist_remove(&room->members,conn); rlist_remove(&conn->rooms,room); + //TODO: notify other members if(room->members.len==0){ room_destroy(room); } @@ -215,114 +205,11 @@ Room* room_find(RList *rlist,const char *gameid,const char *roomid){ } -//Returns line length; reallocates buf if needed. -//If *bufsz==0 || *buf==NULL, allocates buf newly. -//Returns -1 on error or socket closure. -i64 sock_read_line(int sock,char **buf,i64 *bufsz){ - if(*bufsz==0||*buf==NULL){ - *bufsz=512; - *buf=malloc(*bufsz,char); - } - i64 len=0; - while(true){ - if(len==*bufsz-1){ - *bufsz*=2; - *buf=realloc(*buf,*bufsz,char); - } - i64 ret=recv(sock,*buf+len,1,0); - if(ret<=0)return -1; - if((*buf)[len]=='\n'){ - (*buf)[len]='\0'; - return len; - } - len++; - } -} - -//Returns -1 on error or connection closure. -i64 sock_read_data(int sock,char *buf,i64 length){ - i64 got=0; - while(got<length){ - i64 ret=recv(sock,buf+got,length-got,0); - if(ret<=0)return -1; - got+=ret; - } - return 0; -} - -//Returns -1 on error or connection closure. -i64 sock_send_data(int sock,const char *buf,i64 length){ - i64 sent=0; - while(sent<length){ - i64 ret=send(sock,buf+sent,length-sent,0); - if(ret==-1){ - if(errno==EINTR)continue; - return -1; - } - sent+=ret; - } - return 0; -} - -//Returns -1 on error or connection closure. -i64 sock_send(int sock,const char *str){ - return sock_send_data(sock,str,strlen(str)); -} - -//Returns -1 on error or connection closure. -i64 sock_send_line(int sock,const char *str){ - if(sock_send(sock,str)==-1)return -1; - return sock_send(sock,"\n"); -} - -//Returns -1 on error or connection closure. -__attribute__((format (printf,2,3))) -i64 sock_send_line_f(int sock,const char *format,...){ - va_list ap; - va_start(ap,format); - char *buf; - vasprintf(&buf,format,ap); - va_end(ap); - if(buf==NULL)throw("vasprintf: allocation failure"); - i64 ret=sock_send_line(sock,buf); - free(buf); - return ret; -} - -//Returns -1 on error or connection closure. -i64 sock_send_list(int sock,const char *tag,const char *const *list,i64 len){ - char *buf; - asprintf(&buf,"list %s %" PRIi64,tag,len); - if(buf==NULL)throw("asprintf: allocation failure"); - if(len==0){ - i64 ret=sock_send_line(sock,buf); - free(buf); - return ret; - } - - if(sock_send(sock,buf)==-1){free(buf); return -1;} - for(i64 i=0;i<len;i++){ - if(sock_send(sock," ")==-1){free(buf); return -1;} - if(sock_send(sock,list[i])==-1){free(buf); return -1;} - } - return sock_send(sock,"\n"); -} - -//Returns -1 on error or connection closure. -i64 sock_send_int(int sock,const char *tag,i64 value){ - char *buf; - asprintf(&buf,"int %s %" PRIi64,tag,value); - if(buf==NULL)throw("asprintf: allocation failure"); - i64 ret=sock_send_line(sock,buf); - free(buf); - return ret; -} - //Returns -1 on error or connection closure. i64 handle_message(Connection *conn){ static i64 bufsz=0; static char *buf=NULL; - if(sock_read_line(conn->sock,&buf,&bufsz)==-1)return -1; + if(tcp_read_line(conn->sock,&buf,&bufsz)==-1)return -1; char *walker=buf; char *cmdname=strsep(&walker," "); @@ -335,7 +222,7 @@ i64 handle_message(Connection *conn){ if(walker[i]==' ')nargs++; } if(nargs>=10){ - return sock_send_line_f(conn->sock,"error %s Too many arguments",cmdname); + return tcp_send_line_f(conn->sock,"error %s Too many arguments",cmdname); } } char **args=malloc(nargs,char*); @@ -344,70 +231,70 @@ i64 handle_message(Connection *conn){ while((token=strsep(&walker," "))!=NULL)args[nargs++]=token; if(strcmp(cmdname,"ping")==0){ // ping - return sock_send_line(conn->sock,"pong"); + return tcp_send_line(conn->sock,"pong"); } else if(strcmp(cmdname,"room_create")==0){ // room_create gameid roomid public capacity - if(nargs!=4)return sock_send_line(conn->sock,"error room_create Invalid command format"); + if(nargs!=4)return tcp_send_line(conn->sock,"error room_create Invalid command format"); char *endp; i64 capacity=strtol(args[3],&endp,10); if(strlen(args[0])==0||strlen(args[1])==0||strlen(args[2])!=1|| strchr("10",args[2][0])==NULL||args[3][0]=='\0'||*endp!='\0'||capacity<=0){ - return sock_send_line(conn->sock,"error room_create Invalid command format"); + return tcp_send_line(conn->sock,"error room_create Invalid command format"); } char *gameid=strdup(args[0]),*roomid=strdup(args[1]); bool public=args[2][0]=='1'; Room *room=room_add(gameid,roomid,public,capacity); room_join(room,conn); - return sock_send_line(conn->sock,"ok room_create"); + return tcp_send_line(conn->sock,"ok room_create"); } else if(strcmp(cmdname,"room_join")==0){ // room_join gameid roomid - if(nargs!=2)return sock_send_line(conn->sock,"error room_join Invalid command format"); + if(nargs!=2)return tcp_send_line(conn->sock,"error room_join Invalid command format"); if(strlen(args[0])==0||strlen(args[1])==0){ - return sock_send_line(conn->sock,"error room_join Invalid command format"); + return tcp_send_line(conn->sock,"error room_join Invalid command format"); } const char *gameid=args[0],*roomid=args[1]; Room *room=room_find(&rooms,gameid,roomid); - if(room==NULL)return sock_send_line(conn->sock,"error Room not found"); + if(room==NULL)return tcp_send_line(conn->sock,"error Room not found"); if(LIST_FIND(&room->members,conn)!=-1){ - return sock_send_line(conn->sock,"error room_join Already in room"); + return tcp_send_line(conn->sock,"error room_join Already in room"); } if(room->members.len>=room->capacity){ - return sock_send_line(conn->sock,"error room_join Room full"); + return tcp_send_line(conn->sock,"error room_join Room full"); } room_join(room,conn); - return sock_send_line(conn->sock,"ok room_join"); + return tcp_send_line(conn->sock,"ok room_join"); } else if(strcmp(cmdname,"room_leave")==0){ // room_leave gameid roomid - if(nargs!=2)return sock_send_line(conn->sock,"error room_leave Invalid command format"); + if(nargs!=2)return tcp_send_line(conn->sock,"error room_leave Invalid command format"); if(strlen(args[0])==0||strlen(args[1])==0){ - return sock_send_line(conn->sock,"error room_leave Invalid command format"); + return tcp_send_line(conn->sock,"error room_leave Invalid command format"); } const char *gameid=args[0],*roomid=args[1]; Room *room=room_find(&rooms,gameid,roomid); - if(room==NULL)return sock_send_line(conn->sock,"error room_leave Room not found"); - if(LIST_FIND(&room->members,conn)==-1)return sock_send_line(conn->sock,"error room_leave Not in room"); + if(room==NULL)return tcp_send_line(conn->sock,"error room_leave Room not found"); + if(LIST_FIND(&room->members,conn)==-1)return tcp_send_line(conn->sock,"error room_leave Not in room"); room_leave(room,conn); - return sock_send_line(conn->sock,"ok room_leave"); + return tcp_send_line(conn->sock,"ok room_leave"); } else if(strcmp(cmdname,"room_query")==0){ // room_query -> list - if(nargs!=0)return sock_send_line(conn->sock,"error room_query Invalid command format"); + if(nargs!=0)return tcp_send_line(conn->sock,"error room_query Invalid command format"); - if(conn->rooms.len==0)return sock_send_list(conn->sock,"room_query",NULL,0); + if(conn->rooms.len==0)return tcp_send_list(conn->sock,"room_query",NULL,0); const char **idlist=(const char**)malloc(conn->rooms.len,char*); for(i64 i=0;i<conn->rooms.len;i++)idlist[i]=conn->rooms.d[i]->roomid; - i64 ret=sock_send_list(conn->sock,"room_query",idlist,conn->rooms.len); + i64 ret=tcp_send_list(conn->sock,"room_query",idlist,conn->rooms.len); free(idlist); return ret; } else if(strcmp(cmdname,"room_list")==0){ // room_list gameid -> list - if(nargs!=1)return sock_send_line(conn->sock,"error room_list Invalid command format"); + if(nargs!=1)return tcp_send_line(conn->sock,"error room_list Invalid command format"); const char *gameid=args[0]; - if(strlen(gameid)==0)return sock_send_line(conn->sock,"error room_list Invalid command format"); + if(strlen(gameid)==0)return tcp_send_line(conn->sock,"error room_list Invalid command format"); i64 nrooms=0; for(i64 i=0;i<rooms.len;i++){ if(rooms.d[i]->public&&strcmp(rooms.d[i]->gameid,gameid)==0)nrooms++; } - if(nrooms==0)return sock_send_list(conn->sock,"room_list",NULL,0); + if(nrooms==0)return tcp_send_list(conn->sock,"room_list",NULL,0); const char **roomids=(const char**)malloc(nrooms,Room*); nrooms=0; @@ -417,72 +304,72 @@ i64 handle_message(Connection *conn){ roomids[nrooms++]=rooms.d[i]->roomid; } } - i64 ret=sock_send_list(conn->sock,"room_list",roomids,nrooms); + i64 ret=tcp_send_list(conn->sock,"room_list",roomids,nrooms); free(roomids); return ret; } else if(strcmp(cmdname,"room_player_list")==0){ // room_player_list gameid roomid -> list - if(nargs!=2)return sock_send_line(conn->sock,"error room_player_list Invalid command format"); + if(nargs!=2)return tcp_send_line(conn->sock,"error room_player_list Invalid command format"); if(strlen(args[0])==0||strlen(args[1])==0){ - return sock_send_line(conn->sock,"error room_player_list Invalid command format"); + return tcp_send_line(conn->sock,"error room_player_list Invalid command format"); } const char *gameid=args[0],*roomid=args[1]; Room *room=room_find(&rooms,gameid,roomid); - if(room==NULL)return sock_send_line(conn->sock,"error room_player_list Room not found"); - if(LIST_FIND(&room->members,conn)==-1)return sock_send_line(conn->sock,"error room_player_list Not in room"); + if(room==NULL)return tcp_send_line(conn->sock,"error room_player_list Room not found"); + if(LIST_FIND(&room->members,conn)==-1)return tcp_send_line(conn->sock,"error room_player_list Not in room"); - if(room->members.len==0)return sock_send_list(conn->sock,"room_player_list",NULL,0); + if(room->members.len==0)return tcp_send_list(conn->sock,"room_player_list",NULL,0); char **names=malloc(room->members.len,char*); for(i64 i=0;i<room->members.len;i++){ asprintf(&names[i],"%" PRIi64,room->members.d[i]->id); } - i64 ret=sock_send_list(conn->sock,"room_player_list",(const char*const*)names,room->members.len); + i64 ret=tcp_send_list(conn->sock,"room_player_list",(const char*const*)names,room->members.len); for(i64 i=0;i<room->members.len;i++)free(names[i]); free(names); return ret; } else if(strcmp(cmdname,"room_message")==0){ // room_message gameid roomid target length - if(nargs!=4)return sock_send_line(conn->sock,"error room_message Invalid command format"); + if(nargs!=4)return tcp_send_line(conn->sock,"error room_message Invalid command format"); if(strlen(args[0])==0||strlen(args[1])==0){ - return sock_send_line(conn->sock,"error room_message Invalid command format"); + return tcp_send_line(conn->sock,"error room_message Invalid command format"); } const char *gameid=args[0],*roomid=args[1]; Room *room=room_find(&rooms,gameid,roomid); - if(room==NULL)return sock_send_line(conn->sock,"error room_message Room not found"); - if(LIST_FIND(&room->members,conn)==-1)return sock_send_line(conn->sock,"error room_message Not in room"); + if(room==NULL)return tcp_send_line(conn->sock,"error room_message Room not found"); + if(LIST_FIND(&room->members,conn)==-1)return tcp_send_line(conn->sock,"error room_message Not in room"); char *endp; i64 targetid=strtol(args[2],&endp,10); if(args[2][0]=='\0'||*endp!='\0'||targetid<=0){ - return sock_send_line(conn->sock,"error room_message Invalid command format"); + return tcp_send_line(conn->sock,"error room_message Invalid command format"); } i64 msglength=strtol(args[3],&endp,10); if(args[3][0]=='\0'||*endp!='\0'||msglength<0){ - return sock_send_line(conn->sock,"error room_message Invalid command format"); + return tcp_send_line(conn->sock,"error room_message Invalid command format"); } i64 idx; for(idx=0;idx<room->members.len;idx++){ if(room->members.d[idx]->id==targetid)break; } - if(idx==room->members.len)return sock_send_line(conn->sock,"error room_message Target not found"); + if(idx==room->members.len)return tcp_send_line(conn->sock,"error room_message Target not found"); if(msglength==0)return 0; char *buf=malloc(msglength,char); - if(sock_read_data(conn->sock,buf,msglength)==-1){ + if(tcp_read_data(conn->sock,buf,msglength)==-1){ free(buf); return -1; } - sock_send_line_f(room->members.d[idx]->sock,"room_message %" PRIi64 " %" PRIi64,conn->id,msglength); - sock_send_data(room->members.d[idx]->sock,buf,msglength); + tcp_send_line_f(room->members.d[idx]->sock,"room_message %" PRIi64 " %" PRIi64,conn->id,msglength); + tcp_send_data(room->members.d[idx]->sock,buf,msglength); free(buf); - return sock_send_line(conn->sock,"ok room_message"); + return tcp_send_line(conn->sock,"ok room_message"); } else if(strcmp(cmdname,"id")==0){ // id -> int - return sock_send_int(conn->sock,"id",conn->id); + return tcp_send_int(conn->sock,"id",conn->id); } else if(strcmp(cmdname,"exit")==0){ // exit return -1; } else { - return sock_send_line_f(conn->sock,"error %s Unrecognised command",cmdname); + return tcp_send_line_f(conn->sock,"error %s Unrecognised command",cmdname); } } @@ -0,0 +1,191 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdbool.h> +#include <stdarg.h> +#include <string.h> +#include <netdb.h> +#include <sys/socket.h> +#include <errno.h> +#include "global.h" +#include "memory.h" +#include "tcp.h" + +i64 tcp_read_line(int sock,char **buf,i64 *bufsz){ + if(*bufsz==0||*buf==NULL){ + *bufsz=512; + *buf=malloc(*bufsz,char); + } + i64 len=0; + while(true){ + if(len==*bufsz-1){ + *bufsz*=2; + *buf=realloc(*buf,*bufsz,char); + } + i64 ret=recv(sock,*buf+len,1,0); + if(ret<=0)return -1; + if((*buf)[len]=='\n'){ + (*buf)[len]='\0'; + return len; + } + len++; + } +} + +i64 tcp_read_data(int sock,char *buf,i64 length){ + i64 got=0; + while(got<length){ + i64 ret=recv(sock,buf+got,length-got,0); + if(ret<=0)return -1; + got+=ret; + } + return 0; +} + +i64 tcp_send_data(int sock,const char *buf,i64 length){ + i64 sent=0; + while(sent<length){ + i64 ret=send(sock,buf+sent,length-sent,0); + if(ret==-1){ + if(errno==EINTR)continue; + return -1; + } + sent+=ret; + } + return 0; +} + +i64 tcp_send_str(int sock,const char *str){ + return tcp_send_data(sock,str,strlen(str)); +} + +i64 tcp_send_line(int sock,const char *str){ + if(tcp_send_str(sock,str)==-1)return -1; + return tcp_send_str(sock,"\n"); +} + +__attribute__((format (printf,2,3))) +i64 tcp_send_line_f(int sock,const char *format,...){ + va_list ap; + va_start(ap,format); + char *buf; + vasprintf(&buf,format,ap); + va_end(ap); + if(buf==NULL)throw("vasprintf: allocation failure"); + i64 ret=tcp_send_line(sock,buf); + free(buf); + return ret; +} + + +i64 tcp_send_list(int sock,const char *tag,const char *const *list,i64 len){ + char *buf; + asprintf(&buf,"list %s %" PRIi64,tag,len); + if(buf==NULL)throw("asprintf: allocation failure"); + if(len==0){ + i64 ret=tcp_send_line(sock,buf); + free(buf); + return ret; + } + + if(tcp_send_str(sock,buf)==-1){free(buf); return -1;} + for(i64 i=0;i<len;i++){ + if(tcp_send_str(sock," ")==-1){free(buf); return -1;} + if(tcp_send_str(sock,list[i])==-1){free(buf); return -1;} + } + return tcp_send_str(sock,"\n"); +} + +//Returns -1 on error or connection closure. +i64 tcp_send_int(int sock,const char *tag,i64 value){ + char *buf; + asprintf(&buf,"int %s %" PRIi64,tag,value); + if(buf==NULL)throw("asprintf: allocation failure"); + i64 ret=tcp_send_line(sock,buf); + free(buf); + return ret; +} + + +i64 tcp_read_ok(int sock,const char *tag){ + char *buf=NULL; + i64 bufsz=0; + i64 ret=tcp_read_line(sock,&buf,&bufsz); + if(ret==-1){ + if(buf!=NULL)free(buf); + return -1; + } + + char *walker=buf; + char *ok=strsep(&walker," "); + char *realtag=strsep(&walker," "); + bool success=walker==NULL&&strcmp(ok,"ok")==0&&strcmp(realtag,tag)==0; + free(buf); + return success?0:-1; +} + +TcpList* tcp_read_list(int sock,const char *tag){ + char *buf=NULL; + i64 bufsz=0; + i64 ret=tcp_read_line(sock,&buf,&bufsz); + if(ret==-1){ + if(buf!=NULL)free(buf); + return NULL; + } + + char *walker=buf; + char *word1=strsep(&walker," "); + char *word2=strsep(&walker," "); + char *word3=strsep(&walker," "); + char *argstart=walker; + i64 nitems=0; + while((strsep(&walker," "))!=NULL)nitems++; + if(word1==NULL||word2==NULL||word3==NULL||strcmp(word1,"list")!=0||strcmp(word2,tag)!=0){ + free(buf); + return NULL; + } + char *endp; + i64 word3i=strtol(word3,&endp,10); + if(word3[0]=='\0'||*endp!='\0'||word3i!=nitems){ + free(buf); + return NULL; + } + + TcpList *list=malloc(1,TcpList); + list->nitems=nitems; + list->items=malloc(nitems,char*); + walker=argstart; + for(i64 i=0;i<nitems;i++){ + list->items[i]=strdup(strsep(&walker," ")); + } + + return list; +} + +void tcp_list_destroy(TcpList *list){ + free(list->items); + free(list); +} + + +static const char* itoa(int n){ + static char buf[64]; + sprintf(buf,"%d",n); + return buf; +} + + +int tcp_connect(const char *hostname,int port){ + struct addrinfo hints,*res; + memset(&hints,0,sizeof(hints)); + hints.ai_family=AF_UNSPEC; + hints.ai_socktype=SOCK_STREAM; + int ret=getaddrinfo(hostname,itoa(port),&hints,&res); + if(ret!=0){ + return -1; + } + + int sock=socket(res->ai_family,res->ai_socktype,res->ai_protocol); + if(sock==-1)return -1; + if(connect(sock,res->ai_addr,res->ai_addrlen)==-1)return -1; + return sock; +} @@ -0,0 +1,35 @@ +#pragma once + +#include <stdint.h> + +//Unless specified otherwise, functions return -1 on error or socket closure. + +//If *bufsz==0 || *buf==NULL, allocates buf newly. +//Returns line length; reallocates buf if needed. +i64 tcp_read_line(int sock,char **buf,i64 *bufsz); + +i64 tcp_read_data(int sock,char *buf,i64 length); + +i64 tcp_send_data(int sock,const char *buf,i64 length); +i64 tcp_send_str(int sock,const char *str); +i64 tcp_send_line(int sock,const char *str); +i64 tcp_send_line_f(int sock,const char *format,...) __attribute__((format (printf,2,3))); + +i64 tcp_send_list(int sock,const char *tag,const char *const *list,i64 len); +i64 tcp_send_int(int sock,const char *tag,i64 value); + + +i64 tcp_read_ok(int sock,const char *tag); + +typedef struct TcpList{ + i64 nitems; + char **items; +} TcpList; + +//Returns NULL on error instead of -1. +TcpList* tcp_read_list(int sock,const char *tag); +void tcp_list_destroy(TcpList *list); + + +//Returns the socket. +int tcp_connect(const char *hostname,int port); |