198 lines
5.0 KiB
C
198 lines
5.0 KiB
C
#include <errno.h>
|
|
#include <tcl.h>
|
|
#include <json-c/json.h>
|
|
#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);
|
|
}
|