From 7e78000d8966255e919e3d8753fd784f4559ee55 Mon Sep 17 00:00:00 2001 From: simon Date: Tue, 13 Mar 2001 10:22:45 +0000 Subject: [PATCH] Dave Hinton's modifications to the network layer interface, which should make it possible to add SSL support later. git-svn-id: svn://svn.tartarus.org/sgt/putty@996 cda61777-01e9-0310-a592-d414129be87e --- network.h | 126 +++++++++++++++++++++++++++++++++++++++++++++++--------------- raw.c | 30 ++++++++------- rlogin.c | 29 ++++++++------- ssh.c | 32 +++++++++------- telnet.c | 29 ++++++++------- winnet.c | 113 ++++++++++++++++++++++++++++++++++++++++--------------- x11fwd.c | 50 +++++++++++++++---------- 7 files changed, 276 insertions(+), 133 deletions(-) diff --git a/network.h b/network.h index ba1e1df8..f8b81a3c 100644 --- a/network.h +++ b/network.h @@ -3,52 +3,78 @@ * * The way this works is: a back end can choose to open any number * of sockets - including zero, which might be necessary in some. - * It can register a function to be called when data comes in on - * any given one, and it can call the networking abstraction to - * send data without having to worry about blocking. The stuff - * behind the abstraction takes care of selects and nonblocking - * writes and all that sort of painful gubbins. + * It can register a bunch of callbacks (most notably for when + * data is received) for each socket, and it can call the networking + * abstraction to send data without having to worry about blocking. + * The stuff behind the abstraction takes care of selects and + * nonblocking writes and all that sort of painful gubbins. */ #ifndef PUTTY_NETWORK_H #define PUTTY_NETWORK_H -typedef struct Socket_tag *Socket; typedef struct SockAddr_tag *SockAddr; +/* pay attention to levels of indirection */ +typedef struct socket_function_table **Socket; +typedef struct plug_function_table **Plug; + +struct socket_function_table { + Plug (*plug) (Socket s, Plug p); + /* use a different plug (return the old one) */ + /* if p is NULL, it doesn't change the plug */ + /* but it does return the one it's using */ + void (*close) (Socket s); + void (*write) (Socket s, char *data, int len); + void (*write_oob) (Socket s, char *data, int len); + void (*flush) (Socket s); + /* ignored by tcp, but vital for ssl */ + char *(*socket_error) (Socket s); +}; + +struct plug_function_table { + int (*closing) + (Plug p, char *error_msg, int error_code, int calling_back); + /* error_msg is NULL iff it is not an error (ie it closed normally) */ + /* calling_back != 0 iff there is a Plug function */ + /* currently running (would cure the fixme in try_send()) */ + int (*receive) (Plug p, int urgent, char *data, int len); + /* + * - urgent==0. `data' points to `len' bytes of perfectly + * ordinary data. + * + * - urgent==1. `data' points to `len' bytes of data, + * which were read from before an Urgent pointer. + * + * - urgent==2. `data' points to `len' bytes of data, + * the first of which was the one at the Urgent mark. + */ +}; -/* - * This is the function a client must register with each socket, to - * receive data coming in on that socket. The parameter `urgent' - * decides the meaning of `data' and `len': - * - * - urgent==0. `data' points to `len' bytes of perfectly ordinary - * data. - * - * - urgent==1. `data' points to `len' bytes of data, which were - * read from before an Urgent pointer. - * - * - urgent==2. `data' points to `len' bytes of data, the first of - * which was the one at the Urgent mark. - * - * - urgent==3. An error has occurred on the socket. `data' points - * to an error string, and `len' points to an error code. - */ -typedef int (*sk_receiver_t)(Socket s, int urgent, char *data, int len); void sk_init(void); /* called once at program startup */ SockAddr sk_namelookup(char *host, char **canonicalname); void sk_addr_free(SockAddr addr); -Socket sk_new(SockAddr addr, int port, int privport, int oobinline, - sk_receiver_t receiver); -void sk_close(Socket s); -void sk_write(Socket s, char *buf, int len); -void sk_write_oob(Socket s, char *buf, int len); +Socket sk_new(SockAddr addr, int port, int privport, int oobinline, Plug p); + +#define sk_plug(s,p) (((*s)->plug) (s, p)) +#define sk_close(s) (((*s)->close) (s)) +#define sk_write(s,buf,len) (((*s)->write) (s, buf, len)) +#define sk_write_oob(s,buf,len) (((*s)->write_oob) (s, buf, len)) +#define sk_flush(s) (((*s)->flush) (s)) + +#ifdef DEFINE_PLUG_METHOD_MACROS +#define plug_closing(p,msg,code,callback) (((*p)->closing) (p, msg, code, callback)) +#define plug_receive(p,urgent,buf,len) (((*p)->receive) (p, urgent, buf, len)) +#endif /* * Each socket abstraction contains a `void *' private field in * which the client can keep state. + * + * This is perhaps unnecessary now that we have the notion of a plug, + * but there is some existing code that uses it, so it stays. */ void sk_set_private_ptr(Socket s, void *ptr); void *sk_get_private_ptr(Socket s); @@ -59,6 +85,46 @@ void *sk_get_private_ptr(Socket s); * or return NULL if there's no problem. */ char *sk_addr_error(SockAddr addr); -char *sk_socket_error(Socket addr); +#define sk_socket_error(s) (((*s)->socket_error) (s)) + + +/********** SSL stuff **********/ + +/* + * This section is subject to change, but you get the general idea + * of what it will eventually look like. + */ + + +typedef struct certificate *Certificate; +typedef struct our_certificate *Our_Certificate; + /* to be defined somewhere else, somehow */ + +typedef struct ssl_client_socket_function_table **SSL_Client_Socket; +typedef struct ssl_client_plug_function_table **SSL_Client_Plug; + +struct ssl_client_socket_function_table { + struct socket_function_table base; + void (*renegotiate) (SSL_Client_Socket s); + /* renegotiate the cipher spec */ +}; + +struct ssl_client_plug_function_table { + struct plug_function_table base; + int (*refuse_cert) (SSL_Client_Plug p, Certificate cert[]); + /* do we accept this certificate chain? If not, why not? */ + /* cert[0] is the server's certificate, cert[] is NULL-terminated */ + /* the last certificate may or may not be the root certificate */ + Our_Certificate (*client_cert) (SSL_Client_Plug p); + /* the server wants us to identify ourselves */ + /* may return NULL if we want anonymity */ +}; + +SSL_Client_Socket sk_ssl_client_over ( + Socket s, /* pre-existing (tcp) connection */ + SSL_Client_Plug p +); + +#define sk_renegotiate(s) (((*s)->renegotiate) (s)) #endif diff --git a/raw.c b/raw.c index e3d5c06b..7990d1de 100644 --- a/raw.c +++ b/raw.c @@ -24,20 +24,17 @@ static void c_write (char *buf, int len) { from_backend(0, buf, len); } -static int raw_receive (Socket skt, int urgent, char *data, int len) { - if (urgent==3) { +static int raw_closing (Plug plug, char *error_msg, int error_code, int calling_back) { + sk_close(s); + s = NULL; + if (error_msg) { /* A socket error has occurred. */ - sk_close(s); - s = NULL; - connection_fatal(data); - len = 0; - return 0; - } else if (!len) { - /* Connection has closed. */ - sk_close(s); - s = NULL; - return 0; - } + connection_fatal (error_msg); + } /* Otherwise, the remote side closed the connection normally. */ + return 0; +} + +static int raw_receive (Plug plug, int urgent, char *data, int len) { c_write(data, len); return 1; } @@ -50,6 +47,11 @@ static int raw_receive (Socket skt, int urgent, char *data, int len) { * Also places the canonical host name into `realhost'. */ static char *raw_init (char *host, int port, char **realhost) { + static struct plug_function_table fn_table = { + raw_closing, + raw_receive + }, *fn_table_ptr = &fn_table; + SockAddr addr; char *err; @@ -66,7 +68,7 @@ static char *raw_init (char *host, int port, char **realhost) { /* * Open socket. */ - s = sk_new(addr, port, 0, 1, raw_receive); + s = sk_new(addr, port, 0, 1, &fn_table_ptr); if ( (err = sk_socket_error(s)) ) return err; diff --git a/rlogin.c b/rlogin.c index eb6a6c2d..acb27c8d 100644 --- a/rlogin.c +++ b/rlogin.c @@ -25,19 +25,17 @@ static void c_write (char *buf, int len) { from_backend(0, buf, len); } -static int rlogin_receive (Socket skt, int urgent, char *data, int len) { - if (urgent==3) { +static int rlogin_closing (Plug plug, char *error_msg, int error_code, int calling_back) { + sk_close(s); + s = NULL; + if (error_msg) { /* A socket error has occurred. */ - sk_close(s); - s = NULL; - connection_fatal(data); - return 0; - } else if (!len) { - /* Connection has closed. */ - sk_close(s); - s = NULL; - return 0; - } + connection_fatal (error_msg); + } /* Otherwise, the remote side closed the connection normally. */ + return 0; +} + +static int rlogin_receive (Plug plug, int urgent, char *data, int len) { if (urgent == 2) { char c; @@ -77,6 +75,11 @@ static int rlogin_receive (Socket skt, int urgent, char *data, int len) { * Also places the canonical host name into `realhost'. */ static char *rlogin_init (char *host, int port, char **realhost) { + static struct plug_function_table fn_table = { + rlogin_closing, + rlogin_receive + }, *fn_table_ptr = &fn_table; + SockAddr addr; char *err; @@ -93,7 +96,7 @@ static char *rlogin_init (char *host, int port, char **realhost) { /* * Open socket. */ - s = sk_new(addr, port, 1, 0, rlogin_receive); + s = sk_new(addr, port, 1, 0, &fn_table_ptr); if ( (err = sk_socket_error(s)) ) return err; diff --git a/ssh.c b/ssh.c index 549a1e56..3af4ba94 100644 --- a/ssh.c +++ b/ssh.c @@ -1270,21 +1270,20 @@ static void ssh_gotdata(unsigned char *data, int datalen) crFinishV; } -static int ssh_receive(Socket skt, int urgent, char *data, int len) { - if (urgent==3) { +static int ssh_closing (Plug plug, char *error_msg, int error_code, int calling_back) { + ssh_state = SSH_STATE_CLOSED; + sk_close(s); + s = NULL; + if (error_msg) { /* A socket error has occurred. */ - ssh_state = SSH_STATE_CLOSED; - sk_close(s); - s = NULL; - connection_fatal(data); - return 0; - } else if (!len) { - /* Connection has closed. */ - ssh_state = SSH_STATE_CLOSED; - sk_close(s); - s = NULL; - return 0; + connection_fatal (error_msg); + } else { + /* Otherwise, the remote side closed the connection normally. */ } + return 0; +} + +static int ssh_receive(Plug plug, int urgent, char *data, int len) { ssh_gotdata (data, len); if (ssh_state == SSH_STATE_CLOSED) { if (s) { @@ -1303,6 +1302,11 @@ static int ssh_receive(Socket skt, int urgent, char *data, int len) { */ static char *connect_to_host(char *host, int port, char **realhost) { + static struct plug_function_table fn_table = { + ssh_closing, + ssh_receive + }, *fn_table_ptr = &fn_table; + SockAddr addr; char *err; #ifdef FWHACK @@ -1340,7 +1344,7 @@ static char *connect_to_host(char *host, int port, char **realhost) /* * Open socket. */ - s = sk_new(addr, port, 0, 1, ssh_receive); + s = sk_new(addr, port, 0, 1, &fn_table_ptr); if ( (err = sk_socket_error(s)) ) return err; diff --git a/telnet.c b/telnet.c index 65953421..29daf0f1 100644 --- a/telnet.c +++ b/telnet.c @@ -465,19 +465,17 @@ static void do_telnet_read (char *buf, int len) { } } -static int telnet_receive(Socket skt, int urgent, char *data, int len) { - if (urgent==3) { +static int telnet_closing (Plug plug, char *error_msg, int error_code, int calling_back) { + sk_close(s); + s = NULL; + if (error_msg) { /* A socket error has occurred. */ - sk_close(s); - s = NULL; - connection_fatal(data); - return 0; - } else if (!len) { - /* Connection has closed. */ - sk_close(s); - s = NULL; - return 0; - } + connection_fatal (error_msg); + } /* Otherwise, the remote side closed the connection normally. */ + return 0; +} + +static int telnet_receive(Plug plug, int urgent, char *data, int len) { if(urgent) in_synch = TRUE; do_telnet_read (data, len); return 1; @@ -491,6 +489,11 @@ static int telnet_receive(Socket skt, int urgent, char *data, int len) { * Also places the canonical host name into `realhost'. */ static char *telnet_init (char *host, int port, char **realhost) { + static struct plug_function_table fn_table = { + telnet_closing, + telnet_receive + }, *fn_table_ptr = &fn_table; + SockAddr addr; char *err; @@ -507,7 +510,7 @@ static char *telnet_init (char *host, int port, char **realhost) { /* * Open socket. */ - s = sk_new(addr, port, 0, 1, telnet_receive); + s = sk_new(addr, port, 0, 1, &fn_table_ptr); if ( (err = sk_socket_error(s)) ) return err; diff --git a/winnet.c b/winnet.c index f15b136f..01b4f1ba 100644 --- a/winnet.c +++ b/winnet.c @@ -49,6 +49,7 @@ #include #include +#define DEFINE_PLUG_METHOD_MACROS #include "putty.h" #include "network.h" #include "tree234.h" @@ -56,9 +57,11 @@ #define BUFFER_GRANULE 512 struct Socket_tag { + struct socket_function_table *fn; + /* the above variable absolutely *must* be the first in this structure */ char *error; SOCKET s; - sk_receiver_t receiver; + Plug plug; void *private_ptr; struct buffer *head, *tail; int writable; @@ -66,6 +69,16 @@ struct Socket_tag { int oobinline; }; +/* + * We used to typedef struct Socket_tag *Socket. + * + * Since we have made the networking abstraction slightly more + * abstract, Socket no longer means a tcp socket (it could mean + * an ssl socket). So now we must use Actual_Socket when we know + * we are talking about a tcp socket. + */ +typedef struct Socket_tag *Actual_Socket; + struct SockAddr_tag { char *error; /* address family this belongs to, AF_INET for IPv4, AF_INET6 for IPv6. */ @@ -90,7 +103,7 @@ struct buffer { static tree234 *sktree; static int cmpfortree(void *av, void *bv) { - Socket a = (Socket)av, b = (Socket)bv; + Actual_Socket a = (Actual_Socket)av, b = (Actual_Socket)bv; unsigned long as = (unsigned long)a->s, bs = (unsigned long)b->s; if (as < bs) return -1; if (as > bs) return +1; @@ -98,7 +111,7 @@ static int cmpfortree(void *av, void *bv) { } static int cmpforsearch(void *av, void *bv) { - Socket b = (Socket)bv; + Actual_Socket b = (Actual_Socket)bv; unsigned long as = (unsigned long)av, bs = (unsigned long)b->s; if (as < bs) return -1; if (as > bs) return +1; @@ -289,8 +302,37 @@ void sk_addr_free(SockAddr addr) { sfree(addr); } +static Plug sk_tcp_plug (Socket sock, Plug p) { + Actual_Socket s = (Actual_Socket) sock; + Plug ret = s->plug; + if (p) s->plug = p; + return ret; +} + +static void sk_tcp_flush (Socket s) { + /* + * We send data to the socket as soon as we can anyway, + * so we don't need to do anything here. :-) + */ +} + +void sk_tcp_close (Socket s); +void sk_tcp_write (Socket s, char *data, int len); +void sk_tcp_write_oob (Socket s, char *data, int len); +char *sk_tcp_socket_error(Socket s); + Socket sk_new(SockAddr addr, int port, int privport, int oobinline, - sk_receiver_t receiver) { + Plug plug) +{ + static struct socket_function_table fn_table = { + sk_tcp_plug, + sk_tcp_close, + sk_tcp_write, + sk_tcp_write_oob, + sk_tcp_flush, + sk_tcp_socket_error + }; + SOCKET s; #ifdef IPV6 SOCKADDR_IN6 a6; @@ -298,7 +340,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, SOCKADDR_IN a; DWORD err; char *errstr; - Socket ret; + Actual_Socket ret; extern char *do_select(SOCKET skt, int startup); short localport; @@ -306,8 +348,9 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, * Create Socket structure. */ ret = smalloc(sizeof(struct Socket_tag)); + ret->fn = &fn_table; ret->error = NULL; - ret->receiver = receiver; + ret->plug = plug; ret->head = ret->tail = NULL; ret->writable = 1; /* to start with */ ret->sending_oob = 0; @@ -321,7 +364,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, if (s == INVALID_SOCKET) { err = WSAGetLastError(); ret->error = winsock_error_string(err); - return ret; + return (Socket) ret; } ret->oobinline = oobinline; @@ -384,7 +427,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, if (err) { ret->error = winsock_error_string(err); - return ret; + return (Socket) ret; } /* @@ -413,7 +456,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, { err = WSAGetLastError(); ret->error = winsock_error_string(err); - return ret; + return (Socket) ret; } /* Set up a select mechanism. This could be an AsyncSelect on a @@ -421,16 +464,17 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, errstr = do_select(s, 1); if (errstr) { ret->error = errstr; - return ret; + return (Socket) ret; } add234(sktree, ret); - return ret; + return (Socket) ret; } -void sk_close(Socket s) { +static void sk_tcp_close(Socket sock) { extern char *do_select(SOCKET skt, int startup); + Actual_Socket s = (Actual_Socket) sock; del234(sktree, s); do_select(s->s, 0); @@ -442,7 +486,7 @@ void sk_close(Socket s) { * The function which tries to send on a socket once it's deemed * writable. */ -void try_send(Socket s) { +void try_send(Actual_Socket s) { while (s->head) { int nsent; DWORD err; @@ -497,7 +541,9 @@ void try_send(Socket s) { } } -void sk_write(Socket s, char *buf, int len) { +static void sk_tcp_write(Socket sock, char *buf, int len) { + Actual_Socket s = (Actual_Socket) sock; + /* * Add the data to the buffer list on the socket. */ @@ -532,7 +578,9 @@ void sk_write(Socket s, char *buf, int len) { try_send(s); } -void sk_write_oob(Socket s, char *buf, int len) { +static void sk_tcp_write_oob(Socket sock, char *buf, int len) { + Actual_Socket s = (Actual_Socket) sock; + /* * Replace the buffer list on the socket with the data. */ @@ -567,7 +615,7 @@ int select_result(WPARAM wParam, LPARAM lParam) { int ret, open; DWORD err; char buf[BUFFER_GRANULE]; - Socket s; + Actual_Socket s; u_long atmark; /* wParam is the socket itself */ @@ -578,9 +626,9 @@ int select_result(WPARAM wParam, LPARAM lParam) { if ((err = WSAGETSELECTERROR(lParam)) != 0) { /* * An error has occurred on this socket. Pass it to the - * receiver function. + * plug. */ - return s->receiver(s, 3, winsock_error_string(err), err); + return plug_closing (s->plug, winsock_error_string(err), err, 0); } noise_ultralight(lParam); @@ -615,9 +663,11 @@ int select_result(WPARAM wParam, LPARAM lParam) { } } if (ret < 0) { - return s->receiver(s, 3, winsock_error_string(err), err); + return plug_closing (s->plug, winsock_error_string(err), err, 0); + } else if (0 == ret) { + return plug_closing (s->plug, NULL, 0, 0); } else { - return s->receiver(s, atmark ? 0 : 1, buf, ret); + return plug_receive (s->plug, atmark ? 0 : 1, buf, ret); } break; case FD_OOB: @@ -633,7 +683,7 @@ int select_result(WPARAM wParam, LPARAM lParam) { fatalbox(ret == 0 ? "Internal networking trouble" : winsock_error_string(WSAGetLastError())); } else { - return s->receiver(s, 2, buf, ret); + return plug_receive (s->plug, 2, buf, ret); } break; case FD_WRITE: @@ -649,9 +699,11 @@ int select_result(WPARAM wParam, LPARAM lParam) { err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) break; - return s->receiver(s, 3, winsock_error_string(err), err); - } else - open &= s->receiver(s, 0, buf, ret); + return plug_closing (s->plug, winsock_error_string(err), err, 0); + } else { + if (ret) open &= plug_receive (s->plug, 0, buf, ret); + else open &= plug_closing (s->plug, NULL, 0, 0); + } } while (ret > 0); return open; } @@ -663,10 +715,12 @@ int select_result(WPARAM wParam, LPARAM lParam) { * Each socket abstraction contains a `void *' private field in * which the client can keep state. */ -void sk_set_private_ptr(Socket s, void *ptr) { +void sk_set_private_ptr(Socket sock, void *ptr) { + Actual_Socket s = (Actual_Socket) sock; s->private_ptr = ptr; } -void *sk_get_private_ptr(Socket s) { +void *sk_get_private_ptr(Socket sock) { + Actual_Socket s = (Actual_Socket) sock; return s->private_ptr; } @@ -678,7 +732,8 @@ void *sk_get_private_ptr(Socket s) { char *sk_addr_error(SockAddr addr) { return addr->error; } -char *sk_socket_error(Socket s) { +static char *sk_tcp_socket_error(Socket sock) { + Actual_Socket s = (Actual_Socket) sock; return s->error; } @@ -686,10 +741,10 @@ char *sk_socket_error(Socket s) { * For Plink: enumerate all sockets currently active. */ SOCKET first_socket(enum234 *e) { - Socket s = first234(sktree, e); + Actual_Socket s = first234(sktree, e); return s ? s->s : INVALID_SOCKET; } SOCKET next_socket(enum234 *e) { - Socket s = next234(e); + Actual_Socket s = next234(e); return s ? s->s : INVALID_SOCKET; } diff --git a/x11fwd.c b/x11fwd.c index d0a1f997..8ace77d5 100644 --- a/x11fwd.c +++ b/x11fwd.c @@ -62,12 +62,15 @@ extern void sshfwd_close(void *); extern void sshfwd_write(void *, char *, int); struct X11Private { + struct plug_function_table *fn; + /* the above variable absolutely *must* be the first in this structure */ unsigned char firstpkt[12]; /* first X data packet */ char *auth_protocol; unsigned char *auth_data; int data_read, auth_plen, auth_psize, auth_dlen, auth_dsize; int verified; void *c; /* data used by ssh.c */ + Socket s; }; void x11_close (Socket s); @@ -103,23 +106,22 @@ static int x11_verify(char *proto, unsigned char *data, int dlen) { return 1; } -static int x11_receive (Socket s, int urgent, char *data, int len) { - struct X11Private *pr = (struct X11Private *)sk_get_private_ptr(s); +static int x11_closing (Plug plug, char *error_msg, int error_code, int calling_back) { + struct X11Private *pr = (struct X11Private *) plug; + + /* + * We have no way to communicate down the forwarded connection, + * so if an error occurred on the socket, we just ignore it + * and treat it like a proper close. + */ + sshfwd_close(pr->c); + x11_close(pr->s); + return 1; +} + +static int x11_receive (Plug plug, int urgent, char *data, int len) { + struct X11Private *pr = (struct X11Private *) plug; - if (urgent==3) { - /* - * A socket error has occurred. We have no way to - * communicate this down the forwarded connection, so we'll - * just treat it like a proper close. - */ - len = 0; - } - if (!len) { - /* Connection has closed. */ - sshfwd_close(pr->c); - x11_close(s); - return 1; - } sshfwd_write(pr->c, data, len); return 1; } @@ -131,6 +133,11 @@ static int x11_receive (Socket s, int urgent, char *data, int len) { * also, fills the SocketsStructure */ char *x11_init (Socket *s, char *display, void *c) { + static struct plug_function_table fn_table = { + x11_closing, + x11_receive + }; + SockAddr addr; int port; char *err, *dummy_realhost; @@ -163,16 +170,19 @@ char *x11_init (Socket *s, char *display, void *c) { /* * Open socket. */ - *s = sk_new(addr, port, 0, 1, x11_receive); - if ( (err = sk_socket_error(*s)) ) - return err; - pr = (struct X11Private *)smalloc(sizeof(struct X11Private)); + pr->fn = &fn_table; pr->auth_protocol = NULL; pr->verified = 0; pr->data_read = 0; pr->c = c; + pr->s = *s = sk_new(addr, port, 0, 1, (Plug) pr); + if ( (err = sk_socket_error(*s)) ) { + sfree (pr); + return err; + } + sk_set_private_ptr(*s, pr); sk_addr_free(addr); return NULL; -- 2.11.0