#include #include #include #include "uselect.h" #include "ascon.h" #include "ascon.i" #include "script.h" /* * protocol for SECoP * * Markus Zolliker Feb 2019 */ /* the first item in this linked list contains a partial or empty buffer. all the following are the messages in the queue */ typedef struct secop_msg { struct secop_msg *next; char *message; } SecopMsg; typedef struct secop_buf { SecopMsg *queue; pDynString buffer; } SecopBuf; /*----------------------------------------------------------------------------*/ void SecopKillPrivate(void *private) { SecopBuf *msgbuf = private; SecopMsg *msg, *next; for (msg = msgbuf->queue; msg != NULL; msg = next) { next = msg->next; if (msg->message) { free(msg->message); } free(msg); } DeleteDynString(msgbuf->buffer); free(msgbuf); } /*----------------------------------------------------------------------------*/ int Secop3ReadChars(int fd, char *bytes, int maxbytes) { fd_set rmask; struct timeval tmo = { 0, 0 }; int ret; FD_ZERO(&rmask); FD_SET(fd, &rmask); ret = uselect(fd + 1, &rmask, NULL, NULL, &tmo); if (ret <= 0) return ret; ret = recv(fd, bytes, maxbytes, 0); /* PrintChar(*chr); fflush(stdout); */ if (ret > 0) return ret; if (ret == 0) { errno = ECONNRESET; return ASCON_DISCONNECTED; } return ASCON_RECV_ERROR; } /*----------------------------------------------------------------------------*/ int Secop3ProtHandler(Ascon *a) { int ret; char *result; char *space; char *json; char *specifier; Tcl_Obj *tcllist[3], *tclobj; int l, l1, l2; struct json_object *jobj; char buffer[1024]; char *bytes; char *str; SecopBuf *msgbuf; SecopMsg *msg, **msgp; if (a->state <= AsconConnectDone) { /* handle connect states */ return AsconStdHandler(a); } /* treat incoming messages always */ msgbuf = a->private; if (msgbuf == NULL) { msgbuf = calloc(1, sizeof *msgbuf); a->private = msgbuf; a->killPrivate = SecopKillPrivate; msgbuf->buffer = CreateDynString(60, 65536); } ret = Secop3ReadChars(a->fd, buffer, sizeof buffer - 1); while (ret > 0) { assert(ret < sizeof buffer); buffer[ret] = '\0'; bytes = buffer; str = strchr(bytes, '\n'); while (str) { l = str - bytes; DynStringConcatBytes(msgbuf->buffer, bytes, l); /* append at end of queue */ for (msgp = &msgbuf->queue; *msgp != NULL; msgp = &(*msgp)->next); msg = calloc(1, sizeof *msg); *msgp = msg; msg->message = strdup(GetCharArray(msgbuf->buffer)); DynStringClear(msgbuf->buffer); bytes += l + 1; str = strchr(bytes, '\n'); } DynStringConcat(msgbuf->buffer, bytes); ret = Secop3ReadChars(a->fd, buffer, sizeof buffer - 1); } if (ret < 0) { AsconError(a, "SECoP.rd", errno); } switch (a->state) { case AsconWriting: /* do not skip garbage */ l = GetDynStringLength(a->wrBuffer) - a->wrPos; ret = AsconWriteChars(a->fd, GetCharArray(a->wrBuffer) + a->wrPos, l); if (ret < 0) { AsconError(a, "SECoP.wr", errno); /* sets state to AsconFailed */ } else { a->wrPos += ret; if (a->wrPos >= GetDynStringLength(a->wrBuffer)) { a->state = AsconWriteDone; } } return 1; case AsconWriteStart: if (GetDynStringLength(a->wrBuffer) == 0) { /* do not send empty message */ a->state = AsconWriteDone; return 1; } if (a->sendTerminator) { DynStringConcat(a->wrBuffer, a->sendTerminator); } a->wrPos = 0; a->state = AsconWriting; break; case AsconReadStart: a->state = AsconReadDone; msgbuf = a->private; if (msgbuf->queue == NULL) { DynStringClear(a->rdBuffer); return 1; } msg = msgbuf->queue; msgbuf->queue = msg->next; space = strchr(msg->message, ' '); jobj = NULL; specifier = ""; if (space) { l1 = space - msg->message; specifier = space + 1; json = strchr(specifier, ' '); if (json) { l2 = json - specifier; json++; jobj = json_tokener_parse(json); } else { l2 = strlen(specifier); } } else { l1 = strlen(msg->message); l2 = 0; } tcllist[0] = Tcl_NewStringObj(msg->message, l1); tcllist[1] = Tcl_NewStringObj(specifier, l2); if (jobj) { tcllist[2] = json2tcl(jobj); tclobj = Tcl_NewListObj(3, tcllist); } else { tclobj = Tcl_NewListObj(2, tcllist); } DynStringCopy(a->rdBuffer, Tcl_GetString(tclobj)); Tcl_DecrRefCount(tclobj); /* this line avoids a big memory leak! */ a->responseValid = 1; json_object_put(jobj); /* free jobj */ free(msg->message); free(msg); break; } return 1; } /*----------------------------------------------------------------------------*/ void AddSecop3Protocol() { static AsconProtocol secopprot; secopprot.name = "SECoP3"; secopprot.handler = Secop3ProtHandler; secopprot.init = AsconStdInit; AsconInsertProtocol(&secopprot); }