added timeout, improved error handling, added partial message signing

master
Avril 6 years ago
parent 2147a73c4d
commit 9e70ef6e93
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -15,6 +15,7 @@ Additional tools can be built by running `make all-ffi' in the libsipc/ director
Functions: Functions:
;server functions ;server functions
sipc:bind(file) ;; Bind to socket `file'. Returns the socket descriptor on success and nil on fail (the socket must not already exist) sipc:bind(file) ;; Bind to socket `file'. Returns the socket descriptor on success and nil on fail (the socket must not already exist)
sipc:timeout(socket sec) ;; Set read timeout (in seconds) to socket. (if value is NIL, nothing happens, if value is 0, set to infinite). (note: when something fails timeout it sends error :MESSAGE (invalid message).
sipc:release(socket) ;; Close socket sipc:release(socket) ;; Close socket
sipc:hook(socket error-callback message-callback) sipc:hook(socket error-callback message-callback)
;; start listening on `socket'. error-callback should be in format (lambda (error) ...). message-callback should be in format (lambda (type message )...). ;; start listening on `socket'. error-callback should be in format (lambda (error) ...). message-callback should be in format (lambda (type message )...).
@ -128,7 +129,7 @@ Terminal 1:
--- ---
TODO: fix potential hanging TODO: Add proper signing.
TODO: Internal error handling. TODO: Internal error handling.
TODO: Have libsipc built on system load TODO: Have libsipc built on system load
TODO: better .so loading TODO: better .so loading

@ -4,7 +4,7 @@
:description "AF_UNIX IPC for CL" :description "AF_UNIX IPC for CL"
:author "Avril <flanchan@cumallover.me>" :author "Avril <flanchan@cumallover.me>"
:license "None" :license "None"
:version "0.0.2" :version "0.0.3"
:serial t :serial t
:depends-on ( :cffi ) :depends-on ( :cffi )
:components ((:file "package") :components ((:file "package")

@ -8,15 +8,22 @@
;; wrappers ;; wrappers
(defun bind (file) (defun timeout (sd value)
"set timeout for this socket (0 is infinite)"
(when value
(si-timeout sd value))
sd)
(defun bind (file &key (read-timeout nil))
"bind to the AF_UNIX socket `file' "bind to the AF_UNIX socket `file'
returns the sd on success, nil on failure returns the sd on success, nil on failure.
timeout can set a read timeout for the connection. 0 (or nil) is forever.
(remember it has to be deleted first!) (remember it has to be deleted first!)
" "
(let ((rc (si-bind file))) (let ((rc (si-bind file)))
(if (< rc 0) ;error (if (< rc 0) ;error
nil nil
rc))) (timeout rc read-timeout))))
(defun hook (sd on-err on-msg) (defun hook (sd on-err on-msg)
"listen on socket `sd' "listen on socket `sd'
@ -30,7 +37,7 @@
(note: this function blocks until the connection closes) (note: this function blocks until the connection closes)
" "
(let ((*on-message* on-msg) (let ((*on-message* on-msg)
(*on-err* on-err)) (*on-error* on-err))
(let ((rc (si-listen sd (callback si-error-callback) (callback si-callback)))) (let ((rc (si-listen sd (callback si-error-callback) (callback si-callback))))
(if (< rc 0) (if (< rc 0)
nil nil
@ -41,12 +48,12 @@
(si-close sd) (si-close sd)
t) t)
(defun connect (file) (defun connect (file &key (read-timeout nil))
"connect to socket `file'" "connect to socket `file'"
(let ((rc (si-connect file))) (let ((rc (si-connect file)))
(if (< rc 0) ;error (if (< rc 0) ;error
nil nil
rc))) (timeout rc read-timeout))))
(defmacro with-bound-socket (desc &body body) (defmacro with-bound-socket (desc &body body)
"bind socket, run `body', then close the socket. "bind socket, run `body', then close the socket.
@ -82,6 +89,7 @@
:partial - Could not write whole message :partial - Could not write whole message
:error - send() error :error - send() error
:failure - send failed :failure - send failed
:message - invalid response message
:unknown - unknown error code :unknown - unknown error code
:unknown-type - key argument :type is unknown :unknown-type - key argument :type is unknown
:type can be: :type can be:

@ -147,4 +147,6 @@
(defcfun "siqr_close" :int (defcfun "siqr_close" :int
(sd :pointer)) (sd :pointer))
(defcfun "si_timeout" :void
(sd :int)
(secs :int))

@ -24,10 +24,13 @@ typedef enum {
#define SI_NORESP (1<<0) #define SI_NORESP (1<<0)
#define SI_DISCARD (1<<1) #define SI_DISCARD (1<<1)
#define _SI_HEADER_CHECK 0xbeefbeefabad1dea;
typedef struct { typedef struct {
si_type type; si_type type;
unsigned int flags; unsigned int flags;
unsigned int data_len; unsigned int data_len;
unsigned long check;
unsigned char data[]; unsigned char data[];
} si_message; } si_message;
@ -49,12 +52,15 @@ int si_bind(const char* file); //Returns sd, or -1 on failure.
int si_listen(int sd, si_error_callback on_error, si_callback on_message); //Returns non-zero on clean exit, -1 on error. int si_listen(int sd, si_error_callback on_error, si_callback on_message); //Returns non-zero on clean exit, -1 on error.
void si_close(int sd); //Close sock (must be called after si_listen() void si_close(int sd); //Close sock (must be called after si_listen()
void si_timeout(int sd, int seconds); //Set sock timeout (0 for infinite).
char* si_error_string(si_error err); char* si_error_string(si_error err);
char* si_type_string(si_type ty); char* si_type_string(si_type ty);
int si_connect(const char* file); //Returns sd, or -1 on failure. int si_connect(const char* file); //Returns sd, or -1 on failure.
int si_sendmsg_r(int sd, const si_message* msg, si_message** response); //si_sendmmsg but optionally receive response (make sure to free() *response after you're done, NULL to discard response, if there is no response *response is not modified) int si_sendmsg_r(int sd, const si_message* msg, si_message** response); //si_sendmmsg but optionally receive response (make sure to free() *response after you're done, NULL to discard response, if there is no response *response is not modified)
int si_sendmsg(int sd, const si_message *msg); //Returns 0 on okay, 1 if whole message could not be sent, -1 on send error, -2 on weird send error. (see SI_SEND_*) int si_sendmsg(int sd, const si_message *msg); //Returns 0 on okay, 1 if whole message could not be sent, -1 on send error, -2 on weird send error. (see SI_SEND_*)
void si_sign(si_message *msg); //Sign message. Use this before sending one.
//Quick funcs //Quick funcs
int siqs_string_r(int sd, const char* string, unsigned int flags, si_message** resp); //quick send string int siqs_string_r(int sd, const char* string, unsigned int flags, si_message** resp); //quick send string

@ -2,6 +2,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h>
#include <string.h> #include <string.h>
int silent_level=0; int silent_level=0;
@ -41,6 +42,7 @@ char* strbin(const unsigned char* data, size_t sz)
} }
int server_aresp =0; int server_aresp =0;
int timeout=0;
char* textmessage(const si_message *msg) char* textmessage(const si_message *msg)
{ {
@ -122,6 +124,11 @@ int server(const char* bindto, int secho)
Eprintf("error binding\n"); Eprintf("error binding\n");
} else { } else {
Vprintf("bind ok\n"); Vprintf("bind ok\n");
if(timeout)
{
si_timeout(sd, timeout);
Vprintf("timeout set to %d\n", timeout);
}
rc = si_listen(sd, &on_error, &on_message); rc = si_listen(sd, &on_error, &on_message);
Vprintf("listen stopped with rc %d\n", rc); Vprintf("listen stopped with rc %d\n", rc);
if(rc>=0) //positive rc is okay if(rc>=0) //positive rc is okay
@ -172,7 +179,12 @@ int client(const char* conto, const char* string, int bin)
Eprintf("connect error\n"); Eprintf("connect error\n");
} else { } else {
Vprintf("connect ok\n"); Vprintf("connect ok\n");
if(timeout) {
si_timeout(sd, timeout);
Vprintf("timeout set to %d\n", timeout);
}
si_message *response = NULL; si_message *response = NULL;
si_sign(msg);
int rrc = si_sendmsg_r(sd, msg, &response); int rrc = si_sendmsg_r(sd, msg, &response);
if(response) { if(response) {
if(silent_level>1) if(silent_level>1)
@ -204,7 +216,12 @@ int client_close(const char* conto)
Eprintf("connect error\n"); Eprintf("connect error\n");
} else { } else {
Vprintf("connect ok\n"); Vprintf("connect ok\n");
if(timeout) {
si_timeout(sd, timeout);
Vprintf("timeout set to %d\n", timeout);
}
si_message* response = NULL; si_message* response = NULL;
si_sign(msg);
int rrc = si_sendmsg_r(sd, msg, &response); int rrc = si_sendmsg_r(sd, msg, &response);
if(response) { if(response) {
if(silent_level>1) if(silent_level>1)
@ -226,6 +243,7 @@ int pfa(char** argv)
{ {
char * carg = argv[1]+1; char * carg = argv[1]+1;
int rc=0; int rc=0;
int nrc=0;
while (*carg) while (*carg)
{ {
switch(*carg) { switch(*carg) {
@ -238,6 +256,10 @@ int pfa(char** argv)
case 'v': case 'v':
silent_level =-1; silent_level =-1;
break; break;
case 't':
nrc+=1;
timeout = atoi(*(argv+1+nrc));
break;
default: default:
carg++; carg++;
continue; continue;
@ -245,6 +267,23 @@ int pfa(char** argv)
rc = 1; rc = 1;
carg++; carg++;
} }
return rc+nrc;
}
int hang(const char* sock)
{
int sd = si_connect(sock);
int rc =0;
if(sd<0) {
Eprintf("connection failed\n");
rc=-1;
} else {
Vprintf("connection established, waiting for %d seconds\n", (timeout?timeout:5) );
//si_timeout(sd, 5);
sleep(timeout?timeout:5);
Vprintf("abandoning\n");
}
return rc; return rc;
} }
@ -293,6 +332,10 @@ int main(int argc, char** argv)
if(rc!=0) if(rc!=0)
Eprintf("client rc %d\n", rc); Eprintf("client rc %d\n", rc);
break; break;
case 'h':
rc = hang(argv[2]);
Vprintf("hang rc %d\n", rc);
break;
default: default:
Eprintf("i don't know how to do that\n"); Eprintf("i don't know how to do that\n");
break; break;
@ -300,14 +343,16 @@ int main(int argc, char** argv)
} else } else
{ {
argv = _av; argv = _av;
printf("usage: %s [-qQv] -l[fe] <socket>\nusage: %s [-qQv] -p[b] <socket> <message>\nusage: %s [-qQv] -c[f] <socket>\n", argv[0], argv[0], argv[0]); printf("usage: %s [-qQvt] [<timeout>] -l[fe] <socket>\nusage: %s [-qQvt] [<timeout>] -p[b] <socket> <message>\nusage: %s [-qQvt] [<timeout>] -c[f] <socket>\nusage: %s [-qQvt] [<timeout>] -h <socket>\n", argv[0], argv[0], argv[0], argv[0]);
printf("\n-l[fe]\tlisten on socket. (f to unlink file first, e to send response)\n"); printf("\n-l[fe]\tlisten on socket. (f to unlink file first, e to send response)\n");
printf("-p[b]\twrite to socket. (b to send as binary)\n"); printf("-p[b]\twrite to socket. (b to send as binary)\n");
printf("-c[f]\tsend cose signal to socket. (f to unlink file after)\n"); printf("-c[f]\tsend cose signal to socket. (f to unlink file after)\n");
printf("-h\thang this socket (for 5 seconds, or set timeout if there is one)\n");
printf("\nother options:\n"); printf("\nother options:\n");
printf(" -q\tquiet mode, don't print errors\n"); printf(" -q\tquiet mode, don't print errors\n");
printf(" -Q\tsilent mode, only print responses\n"); printf(" -Q\tsilent mode, only print responses\n");
printf(" -v\tverbose mode, print additional messages\n"); printf(" -v\tverbose mode, print additional messages\n");
printf(" -t\tset socket timeout to next arg (in seconds)\n");
} }
return rc; return rc;
} }

@ -5,6 +5,7 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/un.h> #include <sys/un.h>
#include <string.h> #include <string.h>
#include <errno.h>
#include <stdarg.h> #include <stdarg.h>
#include <sipc.h> #include <sipc.h>
@ -44,7 +45,7 @@ int si_bind(const char* file)
static int _si_valid_header(const si_message *msg) static int _si_valid_header(const si_message *msg)
{ {
int kk = (int)msg->type; int kk = (int)msg->type;
return kk>=0 && kk<_SI_ERROR && msg->data_len < SI_MAX_MESSAGE_SIZE; return kk>=0 && kk<_SI_ERROR && msg->data_len < SI_MAX_MESSAGE_SIZE && msg->check == _SI_HEADER_CHECK;
} }
static int _si_read_rest(int sd, si_message *message) static int _si_read_rest(int sd, si_message *message)
@ -70,11 +71,31 @@ int si_listen(int sd, si_error_callback on_error, si_callback on_message)
return -1; return -1;
} }
struct timeval t;
socklen_t len = sizeof(t);
getsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, &t, &len);
//printf("bind timeout : %ld\n", t.tv_sec);
#define ISTSET(t) (!!(t.tv_sec+t.tv_usec))
if(ISTSET(t)) {
//Do we want to keep the accept timeout?
struct timeval t;
t.tv_usec=0;
t.tv_sec=0;
setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
}
while(1) { while(1) {
int csd = accept(sd, NULL,NULL); int csd = accept(sd, NULL,NULL);
if(csd<0) { if(csd<0) {
rc = on_error(SIE_ACCEPT); rc = on_error(SIE_ACCEPT);
if(rc<0) break; if(rc<0) break;
else continue;
}
if(ISTSET(t)) {
setsockopt(csd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
} }
unsigned char buffer[sizeof(si_message)]; unsigned char buffer[sizeof(si_message)];
si_message *message = (si_message*)buffer; si_message *message = (si_message*)buffer;
@ -96,6 +117,7 @@ int si_listen(int sd, si_error_callback on_error, si_callback on_message)
break; break;
} }
} }
//printf("%d: %s\n", errno, strerror(errno));
if(rc<0) { if(rc<0) {
close(csd); close(csd);
break; break;
@ -137,6 +159,7 @@ int si_listen(int sd, si_error_callback on_error, si_callback on_message)
resp->type = SI_BINARY; resp->type = SI_BINARY;
resp->flags = SI_DISCARD | SI_NORESP; resp->flags = SI_DISCARD | SI_NORESP;
resp->data_len = 0; resp->data_len = 0;
si_sign(resp);
int rc2 = si_sendmsg(csd, resp); int rc2 = si_sendmsg(csd, resp);
if (rc2 != SI_SEND_OKAY) if (rc2 != SI_SEND_OKAY)
@ -155,6 +178,8 @@ int si_listen(int sd, si_error_callback on_error, si_callback on_message)
close(csd); close(csd);
if(rc!=0) break; if(rc!=0) break;
} }
if(ISTSET(t))
setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
return rc; return rc;
} }
@ -268,7 +293,7 @@ int si_read_response(int sd, si_message** resp)
break; break;
} }
} }
if(rc==0) if(rc==0 && _si_valid_header(head))
{ {
si_message *full = malloc(sizeof(si_message)+head->data_len+1); si_message *full = malloc(sizeof(si_message)+head->data_len+1);
memset(full,0,sizeof(si_message)+head->data_len+1); memset(full,0,sizeof(si_message)+head->data_len+1);
@ -288,10 +313,19 @@ int si_read_response(int sd, si_message** resp)
free(full); free(full);
return (int)SIE_PCONCLS; return (int)SIE_PCONCLS;
} }
} } else if(rc==0)
rc = (int)SIE_INVALID;
return rc; return rc;
} }
void si_timeout(int sd, int seconds)
{
struct timeval t;
t.tv_sec = seconds;
t.tv_usec=0;
setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &t, sizeof(t));
}
static int _si_sendmsg(int sd, const si_message* msg) static int _si_sendmsg(int sd, const si_message* msg)
{ {
int rc = send(sd, msg, sizeof(si_message)+msg->data_len, 0); int rc = send(sd, msg, sizeof(si_message)+msg->data_len, 0);
@ -305,6 +339,11 @@ static int _si_sendmsg(int sd, const si_message* msg)
return SI_SEND_OKAY; return SI_SEND_OKAY;
} }
void si_sign(si_message* msg)
{
msg->check = _SI_HEADER_CHECK;
}
int si_sendmsg_r(int sd, const si_message *msg, si_message** resp) int si_sendmsg_r(int sd, const si_message *msg, si_message** resp)
{ {
int rc = _si_sendmsg(sd, msg); int rc = _si_sendmsg(sd, msg);
@ -366,6 +405,7 @@ int siqs_string_r(int sd, const char* string, unsigned int flags, si_message** r
memcpy(msg->data, string, msg->data_len); memcpy(msg->data, string, msg->data_len);
si_sign(msg);
int rc = si_sendmsg_r(sd, msg, resp); int rc = si_sendmsg_r(sd, msg, resp);
free(msg); free(msg);
@ -386,6 +426,7 @@ int siqs_close_r(int sd, unsigned int flags, si_message** resp)
msg->data_len=0; msg->data_len=0;
msg->flags = flags; msg->flags = flags;
si_sign(msg);
int rc = si_sendmsg_r(sd, msg, resp); int rc = si_sendmsg_r(sd, msg, resp);
free(msg); free(msg);
@ -407,6 +448,7 @@ int siqs_binary_r(int sd, const unsigned char* buffer, size_t size, unsigned int
memcpy(msg->data, buffer, msg->data_len); memcpy(msg->data, buffer, msg->data_len);
si_sign(msg);
int rc = si_sendmsg_r(sd, msg, resp); int rc = si_sendmsg_r(sd, msg, resp);
free(msg); free(msg);
@ -447,6 +489,7 @@ int siqr_string(const si_message* sd, const char* string)
memcpy(msg->data, string, msg->data_len); memcpy(msg->data, string, msg->data_len);
si_sign(msg);
int rc = si_response(sd, msg); int rc = si_response(sd, msg);
free(msg); free(msg);
@ -463,6 +506,7 @@ int siqr_binary(const si_message* sd, const unsigned char* buffer, size_t size)
memcpy(msg->data, buffer, msg->data_len); memcpy(msg->data, buffer, msg->data_len);
si_sign(msg);
int rc = si_response(sd, msg); int rc = si_response(sd, msg);
free(msg); free(msg);
@ -477,6 +521,7 @@ int siqr_close(const si_message* sd)
msg->type = SI_CLOSE; msg->type = SI_CLOSE;
msg->data_len = 0; msg->data_len = 0;
si_sign(msg);
int rc = si_response(sd, msg); int rc = si_response(sd, msg);
free(msg); free(msg);

@ -4,12 +4,13 @@
(ql:quickload :cl-sipc)) (ql:quickload :cl-sipc))
(defparameter *socket-file* "sipc.socket") (defparameter *socket-file* "sipc.socket")
(defparameter *timeout* 5) ;; read timeout in seconds.
(defparameter *respond* t) ;; should the server echo responses to client? (defparameter *respond* t) ;; should the server echo responses to client?
(when (probe-file *socket-file*) (when (probe-file *socket-file*)
(delete-file *socket-file*)) (delete-file *socket-file*))
(defparameter *socket* (cl-sipc:bind *socket-file*)) ;;attempt to bind to this file (defparameter *socket* (cl-sipc:bind *socket-file* :read-timeout *timeout*)) ;;attempt to bind to this file
(when (not *socket*) (when (not *socket*)
(format t "[e] binding failed ~a~%" *socket-file*) (quit)) (format t "[e] binding failed ~a~%" *socket-file*) (quit))
@ -19,8 +20,9 @@
;;block until the listener is done ;;block until the listener is done
(let ((rc (cl-sipc:hook *socket* (let ((rc (cl-sipc:hook *socket*
#'(lambda (err) ;; Callback ran if there is an error #'(lambda (err) ;; Callback ran if there is an error
(format t "Error: ~a~%" err) (format t "[e] <- ~a~%" err)
nil) ;;returning NIL to the listener stops (force-output)
t) ;;returning NIL to the listener stops, t lets it continue
#'(lambda (type message) ;; Callback ran when a message is received #'(lambda (type message) ;; Callback ran when a message is received
(when *respond* (when *respond*
(format t (format t

Loading…
Cancel
Save