/* TN5250 - An implementation of the 5250 telnet protocol.
* Copyright (C) 2001-2003 Scott Klement
*
* parts of this were copied from telnetstr.c which is (C) 1997 Michael Madore
*
* This file is part of TN5250.
*
* TN5250 is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1, or (at your option)
* any later version.
*
* TN5250 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*
*/
#include "tn5250-private.h"
#ifdef HAVE_LIBSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <time.h>
static int ssl_stream_get_next(Tn5250Stream *This,unsigned char *buf,int size);
static void ssl_stream_do_verb(Tn5250Stream * This, unsigned char verb, unsigned char what);
static int ssl_stream_host_verb(Tn5250Stream * This, unsigned char verb,
unsigned char what);
static void ssl_stream_sb_var_value(Tn5250Buffer * buf, unsigned char *var, unsigned char *value);
static void ssl_stream_sb(Tn5250Stream * This, unsigned char *sb_buf, int sb_len);
static void ssl_stream_escape(Tn5250Buffer * buffer);
static void ssl_stream_write(Tn5250Stream * This, unsigned char *data, int size);
static int ssl_stream_get_byte(Tn5250Stream * This);
static int ssl_stream_connect(Tn5250Stream * This, const char *to);
static int ssl_stream_accept(Tn5250Stream * This, SOCKET_TYPE masterSock);
static void ssl_stream_destroy(Tn5250Stream *This);
static void ssl_stream_disconnect(Tn5250Stream * This);
static int ssl_stream_handle_receive(Tn5250Stream * This);
static void ssl_stream_send_packet(Tn5250Stream * This, int length,
StreamHeader header,
unsigned char *data);
static void tn3270_ssl_stream_send_packet(Tn5250Stream * This, int length,
StreamHeader header,
unsigned char * data);
int ssl_stream_passwd_cb(char *buf, int size, int rwflag, Tn5250Stream *This);
X509 *ssl_stream_load_cert(Tn5250Stream *This, const char *file);
#define SEND 1
#define IS 0
#define INFO 2
#define VALUE 1
#define VAR 0
#define VALUE 1
#define USERVAR 3
#define TERMINAL 1
#define BINARY 2
#define RECORD 4
#define DONE 7
#define HOST 8
#define TRANSMIT_BINARY 0
#define END_OF_RECORD 25
#define TERMINAL_TYPE 24
#define TIMING_MARK 6
#define NEW_ENVIRON 39
#define TN3270E 40
/* Sub-Options for TN3270E negotiation */
#define TN3270E_ASSOCIATE 0
#define TN3270E_CONNECT 1
#define TN3270E_DEVICE_TYPE 2
#define TN3270E_FUNCTIONS 3
#define TN3270E_IS 4
#define TN3270E_REASON 5
#define TN3270E_REJECT 6
#define TN3270E_REQUEST 7
#define TN3270E_SEND 8
/* Reason codes for TN3270E negotiation */
#define TN3270E_CONN_PARTNER 0
#define TN3270E_DEVICE_IN_USE 1
#define TN3270E_INV_ASSOCIATE 2
#define TN3270E_INV_NAME 3
#define TN3270E_INV_DEVICE_TYPE 4
#define TN3270E_TYPE_NAME_ERROR 5
#define TN3270E_UNKNOWN_ERROR 6
#define TN3270E_UNSUPPORTED_REQ 7
/* Function names for TN3270E FUNCTIONS sub-option */
#define TN3270E_BIND_IMAGE 0
#define TN3270E_DATA_STREAM_CTL 1
#define TN3270E_RESPONSES 2
#define TN3270E_SCS_CTL_CODES 3
#define TN3270E_SYSREQ 4
#define EOR 239
#define SE 240
#define SB 250
#define WILL 251
#define WONT 252
#define DO 253
#define DONT 254
#define IAC 255
#define TN5250_STREAM_STATE_NO_DATA 0 /* Dummy state */
#define TN5250_STREAM_STATE_DATA 1
#define TN5250_STREAM_STATE_HAVE_IAC 2
#define TN5250_STREAM_STATE_HAVE_VERB 3 /* e.g. DO, DONT, WILL, WONT */
#define TN5250_STREAM_STATE_HAVE_SB 4 /* SB data */
#define TN5250_STREAM_STATE_HAVE_SB_IAC 5
/* Internal Telnet option settings (bit-wise flags) */
#define RECV_BINARY 1
#define SEND_BINARY 2
#define RECV_EOR 4
#define SEND_EOR 8
#ifndef HAVE_UCHAR
typedef unsigned char UCHAR;
#endif
static const UCHAR hostInitStr[] = {IAC,DO,NEW_ENVIRON,IAC,DO,TERMINAL_TYPE};
static const UCHAR hostDoEOR[] = {IAC,DO,END_OF_RECORD};
static const UCHAR hostDoBinary[] = {IAC,DO,TRANSMIT_BINARY};
static const UCHAR hostDoTN3270E[] = {IAC,DO,TN3270E};
static const UCHAR hostSBDevice[] = {IAC,SB,TN3270E,TN3270E_SEND,TN3270E_DEVICE_TYPE,
IAC,SE};
typedef struct doTable_t {
const UCHAR *cmd;
unsigned len;
} DOTABLE;
static const DOTABLE host5250DoTable[] = {
hostInitStr, sizeof(hostInitStr),
hostDoEOR, sizeof(hostDoEOR),
hostDoBinary, sizeof(hostDoBinary),
NULL, 0
};
static const DOTABLE host3270DoTable[] = {
hostInitStr, sizeof(hostInitStr),
hostDoEOR, sizeof(hostDoEOR),
hostDoBinary, sizeof(hostDoBinary),
NULL, 0
};
static const UCHAR SB_Str_NewEnv[]={IAC, SB, NEW_ENVIRON, SEND, USERVAR,
'I','B','M','R','S','E','E','D', 0,1,2,3,4,5,6,7,
VAR, USERVAR, IAC, SE};
static const UCHAR SB_Str_TermType[]={IAC, SB, TERMINAL_TYPE, SEND, IAC, SE};
/* FIXME: This should be added to Tn5250Stream structure, or something
else better than this :) */
int errnum;
#ifdef NDEBUG
#define IACVERB_LOG(tag,verb,what)
#define TNSB_LOG(sb_buf,sb_len)
#define LOGERROR(tag, ecode)
#define DUMP_ERR_STACK()
#else
#define IACVERB_LOG ssl_log_IAC_verb
#define TNSB_LOG ssl_log_SB_buf
#define LOGERROR ssl_logError
#define DUMP_ERR_STACK ssl_log_error_stack
static char *ssl_getTelOpt(what)
{
char *wcp, wbuf[10];
switch (what) {
case TERMINAL_TYPE:
wcp = "<TERMTYPE>";
break;
case END_OF_RECORD:
wcp = "<END_OF_REC>";
break;
case TRANSMIT_BINARY:
wcp = "<BINARY>";
break;
case NEW_ENVIRON:
wcp = "<NEWENV>";
break;
case EOR:
wcp = "<EOR>";
break;
default:
sprintf(wcp=wbuf, "<%02X>", what);
break;
}
return wcp;
}
static void ssl_log_error_stack(void)
{
FILE *errfp = tn5250_logfile ? tn5250_logfile : stderr;
ERR_print_errors_fp(errfp);
}
static void ssl_logError(char *tag, int ecode)
{
FILE *errfp = tn5250_logfile ? tn5250_logfile : stderr;
fprintf(errfp,"%s: ERROR (code=%d) - %s\n", tag, ecode, strerror(ecode));
}
static void ssl_log_IAC_verb(char *tag, int verb, int what)
{
char *vcp, vbuf[10];
if (!tn5250_logfile)
return;
switch (verb) {
case DO: vcp = "<DO>";
break;
case DONT:
vcp = "<DONT>";
break;
case WILL:
vcp = "<WILL>";
break;
case WONT:
vcp = "<WONT>";
break;
default:
sprintf(vcp=vbuf, "<%02X>", verb);
break;
}
fprintf(tn5250_logfile,"%s:<IAC>%s%s\n", tag, vcp, ssl_getTelOpt(what));
}
static int ssl_dumpVarVal(UCHAR *buf, int len)
{
int c, i;
for (c=buf[i=0]; i<len && c!=VAR && c!=VALUE && c!=USERVAR; c=buf[++i]) {
if (isprint(c))
putc(c, tn5250_logfile);
else
fprintf(tn5250_logfile,"<%02X>", c);
}
return i;
}
static int ssl_dumpNewEnv(unsigned char *buf, int len)
{
int c, i=0, j;
while (i<len) {
switch (c=buf[i]) {
case IAC:
return i;
case VAR:
fputs("\n\t<VAR>",tn5250_logfile);
if (++i<len && buf[i]==USERVAR) {
fputs("<USERVAR>",tn5250_logfile);
return i+1;
}
j = ssl_dumpVarVal(buf+i, len-i);
i += j;
case USERVAR:
fputs("\n\t<USERVAR>",tn5250_logfile);
if (!memcmp("IBMRSEED", &buf[++i], 8)) {
fputs("IBMRSEED",tn5250_logfile);
putc('<',tn5250_logfile);
for (j=0, i+=8; j<8; i++, j++) {
if (j)
putc(' ',tn5250_logfile);
fprintf(tn5250_logfile,"%02X",buf[i]);
}
putc('>',tn5250_logfile);
} else {
j = ssl_dumpVarVal(buf+i, len-i);
i += j;
}
break;
case VALUE:
fputs("<VALUE>",tn5250_logfile);
i++;
j = ssl_dumpVarVal(buf+i, len-i);
i += j;
break;
default:
fputs(ssl_getTelOpt(c),tn5250_logfile);
} /* switch */
} /* while */
return i;
}
static void ssl_log_SB_buf(unsigned char *buf, int len)
{
int c, i, type;
if (!tn5250_logfile)
return;
fprintf(tn5250_logfile,ssl_getTelOpt(type=*buf++));
switch (c=*buf++) {
case IS:
fputs("<IS>",tn5250_logfile);
break;
case SEND:
fputs("<SEND>",tn5250_logfile);
break;
default:
fputs(ssl_getTelOpt(c),tn5250_logfile);
}
len -= 2;
i = (type==NEW_ENVIRON) ? ssl_dumpNewEnv(buf,len) : 0;
while (i<len) {
switch(c=buf[i++]) {
case IAC:
fputs("<IAC>",tn5250_logfile);
if (i<len)
fputs(ssl_getTelOpt(buf[i++]), tn5250_logfile);
break;
default:
if (isprint(c))
putc(c, tn5250_logfile);
else
fprintf(tn5250_logfile,"<%02X>", c);
}
}
}
#endif /* !NDEBUG */
/****f* lib5250/tn5250_ssl_stream_init
* NAME
* tn5250_ssl_stream_init
* SYNOPSIS
* ret = tn5250_ssl_stream_init (This);
* INPUTS
* Tn5250Stream * This -
* DESCRIPTION
* DOCUMENT ME!!!
*****/
int tn5250_ssl_stream_init (Tn5250Stream *This)
{
int len;
char methstr[5];
SSL_METHOD *meth=NULL;
TN5250_LOG(("tn5250_ssl_stream_init() entered.\n"));
/* initialize SSL library */
SSL_load_error_strings();
SSL_library_init();
/* which SSL method do we use? */
strcpy(methstr,"auto");
if (This->config!=NULL && tn5250_config_get (This->config, "ssl_method")) {
strncpy(methstr, tn5250_config_get (This->config, "ssl_method"), 4);
methstr[4] = '\0';
}
if (!strcmp(methstr, "ssl2")) {
meth = SSLv2_client_method();
TN5250_LOG(("SSL Method = SSLv2_client_method()\n"));
} else if (!strcmp(methstr, "ssl3")) {
meth = SSLv3_client_method();
TN5250_LOG(("SSL Method = SSLv3_client_method()\n"));
} else {
meth = SSLv23_client_method();
TN5250_LOG(("SSL Method = SSLv23_client_method()\n"));
}
/* create a new SSL context */
This->ssl_context = SSL_CTX_new(meth);
if (This->ssl_context==NULL) {
DUMP_ERR_STACK ();
return -1;
}
/* if a certificate authority file is defined, load it into this context */
if (This->config!=NULL && tn5250_config_get (This->config, "ssl_ca_file")) {
if (SSL_CTX_load_verify_locations(This->ssl_context,
tn5250_config_get (This->config, "ssl_ca_file"), NULL)<1) {
DUMP_ERR_STACK ();
return -1;
}
}
This->userdata = NULL;
/* if a PEM passphrase is defined, set things up so that it can be used */
if (This->config!=NULL && tn5250_config_get (This->config,"ssl_pem_pass")){
TN5250_LOG(("SSL: Setting password callback\n"));
len = strlen(tn5250_config_get (This->config, "ssl_pem_pass"));
This->userdata = malloc(len+1);
strncpy(This->userdata,
tn5250_config_get (This->config, "ssl_pem_pass"), len);
SSL_CTX_set_default_passwd_cb(This->ssl_context,
(pem_password_cb *)ssl_stream_passwd_cb);
SSL_CTX_set_default_passwd_cb_userdata(This->ssl_context, (void *)This);
}
/* If a certificate file has been defined, load it into this context as well */
if (This->config!=NULL && tn5250_config_get (This->config, "ssl_cert_file")){
if ( tn5250_config_get (This->config, "ssl_check_exp") ) {
X509 *client_cert;
time_t tnow;
int extra_time;
TN5250_LOG(("SSL: Checking expiration of client cert\n"));
client_cert = ssl_stream_load_cert(This,
tn5250_config_get (This->config, "ssl_cert_file"));
if (client_cert == NULL) {
TN5250_LOG(("SSL: Unable to load client certificate!\n"));
return -1;
}
extra_time = tn5250_config_get_int(This->config, "ssl_check_exp");
tnow = time(NULL) + extra_time;
if (ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(client_cert), tnow)
== -1 ) {
if (extra_time > 1) {
printf("SSL error: client certificate will be expired\n");
TN5250_LOG(("SSL: client certificate will be expired\n"));
} else {
printf("SSL error: client certificate has expired\n");
TN5250_LOG(("SSL: client certificate has expired\n"));
}
return -1;
}
X509_free(client_cert);
}
TN5250_LOG(("SSL: Loading certificates from certificate file\n"));
if (SSL_CTX_use_certificate_file(This->ssl_context,
tn5250_config_get (This->config, "ssl_cert_file"),
SSL_FILETYPE_PEM) <= 0) {
DUMP_ERR_STACK ();
return -1;
}
TN5250_LOG(("SSL: Loading private keys from certificate file\n"));
if (SSL_CTX_use_PrivateKey_file(This->ssl_context,
tn5250_config_get (This->config, "ssl_cert_file"),
SSL_FILETYPE_PEM) <= 0) {
DUMP_ERR_STACK ();
return -1;
}
}
This->ssl_handle = NULL;
This->connect = ssl_stream_connect;
This->accept = ssl_stream_accept;
This->disconnect = ssl_stream_disconnect;
This->handle_receive = ssl_stream_handle_receive;
This->send_packet = ssl_stream_send_packet;
This->destroy = ssl_stream_destroy;
This->streamtype = TN5250_STREAM;
TN5250_LOG(("tn5250_ssl_stream_init() success.\n"));
return 0; /* Ok */
}
/****f* lib5250/tn3270_ssl_stream_init
* NAME
* tn3270_ssl_stream_init
* SYNOPSIS
* ret = tn3270_ssl_stream_init (This);
* INPUTS
* Tn5250Stream * This -
* DESCRIPTION
* DOCUMENT ME!!!
*****/
int tn3270_ssl_stream_init (Tn5250Stream *This)
{
int len;
/* initialize SSL library */
SSL_load_error_strings();
SSL_library_init();
/* create a new SSL context */
This->ssl_context = SSL_CTX_new(SSLv23_client_method());
if (This->ssl_context==NULL) {
DUMP_ERR_STACK ();
return -1;
}
/* if a certificate authority file is defined, load it into this context */
if (This->config!=NULL && tn5250_config_get (This->config, "ssl_ca_file")) {
if (SSL_CTX_load_verify_locations(This->ssl_context,
tn5250_config_get (This->config, "ssl_ca_file"), NULL)<1) {
DUMP_ERR_STACK ();
return -1;
}
}
/* if a certificate authority file is defined, load it into this context */
if (This->config!=NULL && tn5250_config_get (This->config, "ssl_ca_file")) {
if (SSL_CTX_load_verify_locations(This->ssl_context,
tn5250_config_get (This->config, "ssl_ca_file"), NULL)<1) {
DUMP_ERR_STACK ();
return -1;
}
}
This->userdata = NULL;
/* if a PEM passphrase is defined, set things up so that it can be used */
if (This->config!=NULL && tn5250_config_get (This->config,"ssl_pem_pass")){
TN5250_LOG(("SSL: Setting password callback\n"));
len = strlen(tn5250_config_get (This->config, "ssl_pem_pass"));
This->userdata = malloc(len+1);
strncpy(This->userdata,
tn5250_config_get (This->config, "ssl_pem_pass"), len);
SSL_CTX_set_default_passwd_cb(This->ssl_context,
(pem_password_cb *)ssl_stream_passwd_cb);
SSL_CTX_set_default_passwd_cb_userdata(This->ssl_context, (void *)This);
}
/* If a certificate file has been defined, load it into this context as well */
if (This->config!=NULL && tn5250_config_get (This->config, "ssl_cert_file")){
TN5250_LOG(("SSL: Loading certificates from certificate file\n"));
if (SSL_CTX_use_certificate_file(This->ssl_context,
tn5250_config_get (This->config, "ssl_cert_file"),
SSL_FILETYPE_PEM) <= 0) {
DUMP_ERR_STACK ();
return -1;
}
TN5250_LOG(("SSL: Loading private keys from certificate file\n"));
if (SSL_CTX_use_PrivateKey_file(This->ssl_context,
tn5250_config_get (This->config, "ssl_cert_file"),
SSL_FILETYPE_PEM) <= 0) {
DUMP_ERR_STACK ();
return -1;
}
}
This->ssl_handle = NULL;
This->connect = ssl_stream_connect;
This->accept = ssl_stream_accept;
This->disconnect = ssl_stream_disconnect;
This->handle_receive = ssl_stream_handle_receive;
This->send_packet = tn3270_ssl_stream_send_packet;
This->destroy = ssl_stream_destroy;
This->streamtype = TN3270E_STREAM;
return 0; /* Ok */
}
/****i* lib5250/ssl_stream_connect
* NAME
* ssl_stream_connect
* SYNOPSIS
* ret = ssl_stream_connect (This, to);
* INPUTS
* Tn5250Stream * This -
* const char * to -
* DESCRIPTION
* Connects to server. The `to' parameter is in the form
* host[:port].
*****/
static int ssl_stream_connect(Tn5250Stream * This, const char *to)
{
struct sockaddr_in serv_addr;
u_long ioctlarg = 1;
char *address;
int r;
X509 *server_cert;
long certvfy;
TN5250_LOG(("tn5250_ssl_stream_connect() entered.\n"));
memset((char *) &serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
/* Figure out the internet address. */
address = (char *)malloc (strlen (to)+1);
strcpy (address, to);
if (strchr (address, ':'))
*strchr (address, ':') = '\0';
serv_addr.sin_addr.s_addr = inet_addr(address);
if (serv_addr.sin_addr.s_addr == INADDR_NONE) {
struct hostent *pent = gethostbyname(address);
if (pent != NULL)
serv_addr.sin_addr.s_addr = *((u_long *) (pent->h_addr));
}
free (address);
if (serv_addr.sin_addr.s_addr == INADDR_NONE) {
TN5250_LOG(("sslstream: Host lookup failed!\n"));
return -1;
}
/* Figure out the port name. */
if (strchr (to, ':') != NULL) {
const char *port = strchr (to, ':') + 1;
serv_addr.sin_port = htons((u_short) atoi(port));
if (serv_addr.sin_port == 0) {
struct servent *pent = getservbyname(port, "tcp");
if (pent != NULL)
serv_addr.sin_port = pent->s_port;
}
if (serv_addr.sin_port == 0) {
TN5250_LOG(("sslstream: Port lookup failed!\n"));
return -1;
}
} else {
/* No port specified ... use telnet-ssl port. */
struct servent *pent = getservbyname ("telnets", "tcp");
if (pent == NULL)
serv_addr.sin_port = htons(992);
else
serv_addr.sin_port = pent->s_port;
}
This->ssl_handle = SSL_new(This->ssl_context);
if (This->ssl_handle==NULL) {
DUMP_ERR_STACK ();
TN5250_LOG(("sslstream: SSL_new() failed!\n"));
return -1;
}
This->sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (WAS_INVAL_SOCK(This->sockfd)) {
TN5250_LOG(("sslstream: socket() failed, errno=%d\n", errno));
return -1;
}
if ((r=SSL_set_fd(This->ssl_handle, This->sockfd))==0) {
errnum = SSL_get_error(This->ssl_handle, r);
DUMP_ERR_STACK ();
TN5250_LOG(("sslstream: SSL_set_fd() failed, errnum=%d\n", errnum));
return errnum;
}
r = connect(This->sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
if (WAS_ERROR_RET(r)) {
TN5250_LOG(("sslstream: connect() failed, errno=%d\n", errno));
return -1;
}
if ((r=SSL_connect(This->ssl_handle)<1)) {
errnum = SSL_get_error(This->ssl_handle, r);
DUMP_ERR_STACK ();
TN5250_LOG(("sslstream: SSL_connect() failed, errnum=%d\n", errnum));
return errnum;
}
TN5250_LOG(("Connected with SSL\n"));
TN5250_LOG(("Using %s cipher with a %d bit secret key\n",
SSL_get_cipher_name(This->ssl_handle),
SSL_get_cipher_bits(This->ssl_handle, NULL) ));
server_cert = SSL_get_peer_certificate (This->ssl_handle);
if (server_cert == NULL) {
TN5250_LOG(("sslstream: Server did not present a certificate!\n"));
return -1;
}
else {
time_t tnow = time(NULL);
int extra_time= 0;
if (This->config!=NULL &&
tn5250_config_get(This->config, "ssl_check_exp")!=NULL) {
extra_time = tn5250_config_get_int(This->config,
"ssl_check_exp");
tnow += extra_time;
if (ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(server_cert), tnow)
== -1 ) {
if (extra_time > 1) {
printf("SSL error: server certificate will be expired\n");
TN5250_LOG(("SSL: server certificate will be expired\n"));
} else {
printf("SSL error: server certificate has expired\n");
TN5250_LOG(("SSL: server certificate has expired\n"));
}
return -1;
}
}
TN5250_LOG(("SSL Certificate issued by: %s\n",
X509_NAME_oneline(X509_get_issuer_name(server_cert), 0,0) ));
certvfy = SSL_get_verify_result(This->ssl_handle);
if (certvfy == X509_V_OK) {
TN5250_LOG(("SSL Certificate successfully verified!\n"));
} else {
TN5250_LOG(("SSL Certificate verification failed, reason: %d\n",
certvfy));
if (This->config!=NULL &&
tn5250_config_get_bool (This->config, "ssl_verify_server"))
return -1;
}
}
/* Set socket to non-blocking mode. */
TN5250_LOG(("SSL must be Non-Blocking\n"));
TN_IOCTL(This->sockfd, FIONBIO, &ioctlarg);
This->state = TN5250_STREAM_STATE_DATA;
TN5250_LOG(("tn5250_ssl_stream_connect() success.\n"));
return 0;
}
/****i* lib5250/ssl_stream_accept
* NAME
* ssl_stream_accept
* SYNOPSIS
* ret = ssl_stream_accept (This, masterSock);
* INPUTS
* Tn5250Stream * This -
* SOCKET masterSock -
* DESCRIPTION
* Accepts a connection from the client.
*****/
static int ssl_stream_accept(Tn5250Stream * This, SOCKET_TYPE masterfd)
{
int i, len, retCode;
struct sockaddr_in serv_addr;
fd_set fdr;
struct timeval tv;
int negotiating;
#ifndef WINELIB
u_long ioctlarg=1L;
#endif
/* FIXME: This routine needs to be converted to use SSL calls
I just left it disabled for now. -- SCK */
#if 0
/*
len = sizeof(serv_addr);
This->sockfd = accept(masterSock, (struct sockaddr *) &serv_addr, &len);
if (WAS_INVAL_SOCK(This->sockfd)) {
return -1;
}
*/
printf("This->sockfd = %d\n", masterfd);
This->sockfd = masterfd;
/* Set socket to non-blocking mode. */
TN_IOCTL(This->sockfd, FIONBIO, &ioctlarg);
This->state = TN5250_STREAM_STATE_DATA;
This->status = HOST;
/* Commence TN5250 negotiations...
Send DO options (New Environment, Terminal Type, etc.) */
if(This->streamtype == TN3270E_STREAM)
{
retCode = SSL_write(this->ssl_handle, hostDoTN3270E, sizeof(hostDoTN3270E));
if (retCode<1) {
errnum = SSL_get_error(This->ssl_handle, retCode);
fprintf(stderr, "sslstream: %s\n", ERR_error_string(errnum,NULL));
return errnum;
}
tv.tv_sec = 5;
tv.tv_usec = 0;
TN_SELECT(This->sockfd + 1, &fdr, NULL, NULL, &tv);
if (FD_ISSET(This->sockfd, &fdr)) {
if (!ssl_stream_handle_receive(This)) {
retCode = errnum;
return retCode ? retCode : -1;
}
} else {
return -1;
}
if(This->streamtype == TN3270E_STREAM)
{
retCode = SSL_write(This->ssl_handle, hostDoTN3270E,sizeof(hostDoTN3270E));
if (retCode<1) {
errnum = SSL_get_error(This->ssl_handle, retCode);
fprintf(stderr,"sslstream: %s\n",ERR_error_string(errnum,NULL));
return errnum;
}
FD_ZERO(&fdr);
FD_SET(This->sockfd, &fdr);
tv.tv_sec = 5;
tv.tv_usec = 0;
TN_SELECT(This->sockfd + 1, &fdr, NULL, NULL, &tv);
if (FD_ISSET(This->sockfd, &fdr)) {
if (!ssl_stream_handle_receive(This)) {
retCode = errnum;
return retCode ? retCode : -1;
}
} else {
return -1;
}
FD_ZERO(&fdr);
FD_SET(This->sockfd, &fdr);
tv.tv_sec = 5;
tv.tv_usec = 0;
TN_SELECT(This->sockfd + 1, &fdr, NULL, NULL, &tv);
if (FD_ISSET(This->sockfd, &fdr)) {
if (!ssl_stream_handle_receive(This)) {
retCode = errnum;
return retCode ? retCode : -1;
}
} else {
return -1;
}
}
else
{
goto neg5250;
}
}
else
{
neg5250:
for (i=0; host5250DoTable[i].cmd; i++) {
retCode = SSL_write(This->ssl_handle, host5250DoTable[i].cmd,
host5250DoTable[i].len);
if (retCode<1) {
errnum = SSL_get_error(This->ssl_handle, retCode);
fprintf(stderr,"sslstream: %s\n",ERR_error_string(errnum,NULL));
return errnum;
}
FD_ZERO(&fdr);
FD_SET(This->sockfd, &fdr);
tv.tv_sec = 5;
tv.tv_usec = 0;
TN_SELECT(This->sockfd + 1, &fdr, NULL, NULL, &tv);
if (FD_ISSET(This->sockfd, &fdr)) {
if (!ssl_stream_handle_receive(This)) {
retCode = errnum;
return retCode ? retCode : -1;
}
} else {
return -1;
}
}
}
return 0;
#endif
return -1;
}
/****i* lib5250/ssl_stream_disconnect
* NAME
* ssl_stream_disconnect
* SYNOPSIS
* ssl_stream_disconnect (This);
* INPUTS
* Tn5250Stream * This -
* DESCRIPTION
* Disconnect from the remote host.
*****/
static void ssl_stream_disconnect(Tn5250Stream * This)
{
SSL_shutdown(This->ssl_handle);
TN_CLOSE(This->sockfd);
}
/****i* lib5250/ssl_stream_destroy
* NAME
* ssl_stream_destroy
* SYNOPSIS
* ssl_stream_destroy (This);
* INPUTS
* Tn5250Stream * This -
* DESCRIPTION
* DOCUMENT ME!!!
*****/
static void ssl_stream_destroy(Tn5250Stream *This)
{
/* noop */
}
/****i* lib5250/ssl_stream_get_next
* NAME
* ssl_stream_get_next
* SYNOPSIS
* ssl_stream_get_next (This, buf, size);
* INPUTS
* Tn5250Stream * This -
* unsigned char * buf -
* int size -
* DESCRIPTION
* Reads data from the socket, returns the length,
* or -2 if disconnected, or -1 if out of data to read.
*****/
static int ssl_stream_get_next(Tn5250Stream *This,unsigned char *buf,int size)
{
int rc;
fd_set wrwait;
/* read data.
*
* Note: it's possible, due to the negotiations that SSL can do below
* the surface, that SSL_read() will need to wait for buffer space
* to write to. If that happens, we'll use select() to wait for
* space and try again.
*/
do {
rc = SSL_read(This->ssl_handle, buf, size);
if (rc < 1) {
errnum = SSL_get_error(This->ssl_handle, rc);
switch (errnum) {
case SSL_ERROR_WANT_WRITE:
FD_ZERO(&wrwait);
FD_SET(This->sockfd, &wrwait);
select(This->sockfd+1, NULL, &wrwait, NULL, NULL);
break;
case SSL_ERROR_WANT_READ:
return -1;
break;
default:
return -2;
break;
}
}
} while (rc<1);
return rc;
}
static int ssl_sendWill(Tn5250Stream *This, unsigned char what)
{
UCHAR buff[3]={IAC,WILL};
buff[2] = what;
TN5250_LOG(("SSL_Write: %x %x %x\n", buff[0], buff[1], buff[2]));
return SSL_write(This->ssl_handle, buff, 3);
}
/****i* lib5250/ssl_stream_host_verb
* NAME
* ssl_stream_host_verb
* SYNOPSIS
* ssl_stream_host_verb (This, verb, what);
* INPUTS
* Tn5250Stream * This -
* unsigned char verb -
* unsigned char what -
* DESCRIPTION
* Process the telnet DO, DONT, WILL, or WONT escape sequence.
*****/
static int ssl_stream_host_verb(Tn5250Stream * This, unsigned char verb,
unsigned char what)
{
int len, option=0, retval=0;
IACVERB_LOG("GotVerb(1)",verb,what);
switch (verb) {
case DO:
switch (what) {
case END_OF_RECORD:
option = SEND_EOR;
break;
case TRANSMIT_BINARY:
option = SEND_BINARY;
break;
default:
break;
} /* DO: switch (what) */
break;
case DONT:
case WONT:
if(what == TN3270E)
{
This->streamtype = TN3270_STREAM;
}
break;
case WILL:
switch (what) {
case NEW_ENVIRON:
len = sizeof(SB_Str_NewEnv);
TN5250_LOG(("Sending SB NewEnv..\n"));
retval = SSL_write(This->ssl_handle, SB_Str_NewEnv, len);
break;
case TERMINAL_TYPE:
len = sizeof(SB_Str_TermType);
TN5250_LOG(("Sending SB TermType..\n"));
retval = SSL_write(This->ssl_handle, SB_Str_TermType, len);
break;
case END_OF_RECORD:
option = RECV_EOR;
retval = ssl_sendWill(This, what);
break;
case TRANSMIT_BINARY:
option = RECV_BINARY;
retval = ssl_sendWill(This, what);
break;
default:
break;
} /* WILL: switch (what) */
break;
default:
break;
} /* switch (verb) */
if (retval>0) retval=option;
return(retval);
} /* ssl_stream_host_verb */
/****i* lib5250/ssl_stream_do_verb
* NAME
* ssl_stream_do_verb
* SYNOPSIS
* ssl_stream_do_verb (This, verb, what);
* INPUTS
* Tn5250Stream * This -
* unsigned char verb -
* unsigned char what -
* DESCRIPTION
* Process the telnet DO, DONT, WILL, or WONT escape sequence.
*****/
static void ssl_stream_do_verb(Tn5250Stream * This, unsigned char verb, unsigned char what)
{
unsigned char reply[3];
int ret;
IACVERB_LOG("GotVerb(2)", verb, what);
reply[0] = IAC;
reply[2] = what;
switch (verb) {
case DO:
switch (what) {
case TERMINAL_TYPE:
case END_OF_RECORD:
case TRANSMIT_BINARY:
case NEW_ENVIRON:
reply[1] = WILL;
break;
default:
reply[1] = WONT;
break;
}
break;
case DONT:
break;
case WILL:
switch (what) {
case TERMINAL_TYPE:
case END_OF_RECORD:
case TRANSMIT_BINARY:
case NEW_ENVIRON:
reply[1] = DO;
break;
case TIMING_MARK:
TN5250_LOG(("do_verb: IAC WILL TIMING_MARK received.\n"));
default:
reply[1] = DONT;
break;
}
break;
case WONT:
break;
}
/* We should really keep track of states here, but the code has been
* like this for some time, and no complaints.
*
* Actually, I don't even remember what that comment means -JMF */
IACVERB_LOG("GotVerb(3)",verb,what);
TN5250_LOG(("SSL_Write: %x %x %x\n", reply[0], reply[1], reply[2]));
ret = SSL_write(This->ssl_handle, (char *) reply, 3);
if (ret<1) {
errnum = SSL_get_error(This->ssl_handle, ret);
printf("Error writing to socket: %s\n", ERR_error_string(errnum,NULL));
exit(5);
}
}
static void ssl_stream_host_sb(Tn5250Stream * This, UCHAR *sb_buf,
int sb_len)
{
int rc;
int i;
int sbType;
int sbParm;
Tn5250Buffer tbuf;
UCHAR deviceResponse[] = {IAC,SB,TN3270E,TN3270E_DEVICE_TYPE,TN3270E_IS};
UCHAR functionResponse[] = {IAC,SB,TN3270E,TN3270E_FUNCTIONS};
char * dummyname = "TN3E002";
if (sb_len <= 0)
return;
TN5250_LOG(("GotSB:<IAC><SB>"));
TNSB_LOG(sb_buf,sb_len);
TN5250_LOG(("<IAC><SE>\n"));
sbType = sb_buf[0];
switch (sbType)
{
case TN3270E:
sb_buf += 1;
sb_len -= 1;
sbParm = sb_buf[0];
switch (sbParm)
{
case TN3270E_DEVICE_TYPE:
sb_buf += 2; /* Device string follows DEVICE_TYPE IS parameter */
sb_len -= 2;
tn5250_buffer_init(&tbuf);
tn5250_buffer_append_data(&tbuf, deviceResponse,
sizeof(deviceResponse));
for(i=0; i<sb_len && sb_buf[i] != IAC; i++)
tn5250_buffer_append_byte(&tbuf, sb_buf[i]);
tn5250_buffer_append_byte(&tbuf, TN3270E_CONNECT);
tn5250_buffer_append_data(&tbuf, dummyname, strlen(dummyname));
tn5250_buffer_append_byte(&tbuf, IAC);
tn5250_buffer_append_byte(&tbuf, SE);
rc = SSL_write(This->ssl_handle, (char *) tn5250_buffer_data(&tbuf),
tn5250_buffer_length(&tbuf));
if (rc<1) {
errnum = SSL_get_error(This->ssl_handle, rc);
printf("Error in SSL_write: %s\n", ERR_error_string(errnum,NULL));
exit(5);
}
break;
case TN3270E_FUNCTIONS:
sb_buf += 2; /* Function list follows FUNCTIONS REQUEST parameter */
sb_len -= 2;
tn5250_buffer_init(&tbuf);
tn5250_buffer_append_data(&tbuf, functionResponse,
sizeof(functionResponse));
tn5250_buffer_append_byte(&tbuf, TN3270E_IS);
for(i=0; i<sb_len && sb_buf[i] != IAC; i++)
{
tn5250_buffer_append_byte(&tbuf, sb_buf[i]);
This->options = This->options | (1 << (sb_buf[i]+1));
}
tn5250_buffer_append_byte(&tbuf, IAC);
tn5250_buffer_append_byte(&tbuf, SE);
rc = SSL_write(This->ssl_handle, (char *) tn5250_buffer_data(&tbuf),
tn5250_buffer_length(&tbuf));
if (rc<1) {
errnum = SSL_get_error(This->ssl_handle, rc);
printf("Error in SSL_write: %s\n", ERR_error_string(errnum,NULL));
exit(5);
}
break;
default:
break;
}
break;
case TERMINAL_TYPE:
sb_buf += 2; /* Assume IS follows SB option type. */
sb_len -= 2;
tn5250_buffer_init(&tbuf);
for (i=0; i<sb_len && sb_buf[i]!=IAC; i++)
tn5250_buffer_append_byte(&tbuf, sb_buf[i]);
tn5250_buffer_append_byte(&tbuf, 0);
tn5250_stream_setenv(This, "TERM", (char *) tbuf.data);
tn5250_buffer_free(&tbuf);
break;
case NEW_ENVIRON:
/* TODO:
* setNewEnvVars(This, sb_buf, sb_len);
*/
break;
default:
break;
} /* switch */
} /* ssl_stream_host_sb */
/****i* lib5250/ssl_stream_sb_var_value
* NAME
* ssl_stream_sb_var_value
* SYNOPSIS
* ssl_stream_sb_var_value (buf, var, value);
* INPUTS
* Tn5250Buffer * buf -
* unsigned char * var -
* unsigned char * value -
* DESCRIPTION
* Utility function for constructing replies to NEW_ENVIRON requests.
*****/
static void ssl_stream_sb_var_value(Tn5250Buffer * buf, unsigned char *var, unsigned char *value)
{
tn5250_buffer_append_byte(buf, VAR);
tn5250_buffer_append_data(buf, var, strlen((char *) var));
tn5250_buffer_append_byte(buf, VALUE);
tn5250_buffer_append_data(buf, value, strlen((char *) value));
}
/****i* lib5250/ssl_stream_sb
* NAME
* ssl_stream_sb
* SYNOPSIS
* ssl_stream_sb (This, sb_buf, sb_len);
* INPUTS
* Tn5250Stream * This -
* unsigned char * sb_buf -
* int sb_len -
* DESCRIPTION
* Handle telnet SB escapes, which are the option-specific negotiations.
*****/
static void ssl_stream_sb(Tn5250Stream * This, unsigned char *sb_buf, int sb_len)
{
Tn5250Buffer out_buf;
int ret;
TN5250_LOG(("GotSB:<IAC><SB>"));
TNSB_LOG(sb_buf,sb_len);
TN5250_LOG(("<IAC><SE>\n"));
tn5250_buffer_init(&out_buf);
if (sb_len <= 0)
return;
if (sb_buf[0] == TERMINAL_TYPE) {
unsigned char *termtype;
if (sb_buf[1] != SEND)
return;
termtype = (unsigned char *) tn5250_stream_getenv(This, "TERM");
tn5250_buffer_append_byte(&out_buf, IAC);
tn5250_buffer_append_byte(&out_buf, SB);
tn5250_buffer_append_byte(&out_buf, TERMINAL_TYPE);
tn5250_buffer_append_byte(&out_buf, IS);
tn5250_buffer_append_data(&out_buf, termtype, strlen((char *) termtype));
tn5250_buffer_append_byte(&out_buf, IAC);
tn5250_buffer_append_byte(&out_buf, SE);
ret = SSL_write(This->ssl_handle, (char *) tn5250_buffer_data(&out_buf),
tn5250_buffer_length(&out_buf));
if (ret<1) {
errnum = SSL_get_error(This->ssl_handle, ret);
printf("Error in SSL_write: %s\n", ERR_error_string(errnum,NULL));
exit(5);
}
TN5250_LOG(("SentSB:<IAC><SB><TERMTYPE><IS>%s<IAC><SE>\n", termtype));
This->status = This->status | TERMINAL;
} else if (sb_buf[0] == NEW_ENVIRON) {
Tn5250ConfigStr *iter;
tn5250_buffer_append_byte(&out_buf, IAC);
tn5250_buffer_append_byte(&out_buf, SB);
tn5250_buffer_append_byte(&out_buf, NEW_ENVIRON);
tn5250_buffer_append_byte(&out_buf, IS);
if (This->config != NULL) {
if ((iter = This->config->vars) != NULL) {
do {
if ((strlen (iter->name) > 4) && (!memcmp (iter->name, "env.", 4))) {
ssl_stream_sb_var_value(&out_buf,
(unsigned char *) iter->name + 4,
(unsigned char *) iter->value);
}
iter = iter->next;
} while (iter != This->config->vars);
}
}
tn5250_buffer_append_byte(&out_buf, IAC);
tn5250_buffer_append_byte(&out_buf, SE);
ret = SSL_write(This->ssl_handle, (char *) tn5250_buffer_data(&out_buf),
tn5250_buffer_length(&out_buf));
if (ret<1) {
errnum = SSL_get_error(This->ssl_handle, ret);
printf("Error in SSL_write: %s\n", ERR_error_string(errnum,NULL));
exit(5);
}
TN5250_LOG(("SentSB:<IAC><SB>"));
TNSB_LOG(&out_buf.data[2], out_buf.len-4);
TN5250_LOG(("<IAC><SE>\n"));
}
tn5250_buffer_free(&out_buf);
}
/****i* lib5250/ssl_stream_get_byte
* NAME
* ssl_stream_get_byte
* SYNOPSIS
* ret = ssl_stream_get_byte (This);
* INPUTS
* Tn5250Stream * This -
* DESCRIPTION
* Returns the next byte from the 5250 data stream, or return -1 if no data
* is waiting on the socket or -2 if disconnected, or -END_OF_RECORD if a
* telnet EOR escape sequence was encountered.
*****/
static int ssl_stream_get_byte(Tn5250Stream * This)
{
unsigned char temp;
unsigned char verb;
do {
if (This->state == TN5250_STREAM_STATE_NO_DATA)
This->state = TN5250_STREAM_STATE_DATA;
This->rcvbufpos ++;
if (This->rcvbufpos >= This->rcvbuflen) {
This->rcvbufpos = 0;
This->rcvbuflen = ssl_stream_get_next(This, This->rcvbuf, TN5250_RBSIZE);
if (This->rcvbuflen<0)
return This->rcvbuflen;
}
temp = This->rcvbuf[This->rcvbufpos];
switch (This->state) {
case TN5250_STREAM_STATE_DATA:
if (temp == IAC)
This->state = TN5250_STREAM_STATE_HAVE_IAC;
break;
case TN5250_STREAM_STATE_HAVE_IAC:
switch(temp) {
case IAC:
This->state = TN5250_STREAM_STATE_DATA;
break;
case DO:
case DONT:
case WILL:
case WONT:
verb = temp;
This->state = TN5250_STREAM_STATE_HAVE_VERB;
break;
case SB:
This->state = TN5250_STREAM_STATE_HAVE_SB;
tn5250_buffer_free(&(This->sb_buf));
break;
case EOR:
This->state = TN5250_STREAM_STATE_DATA;
return -END_OF_RECORD;
default:
TN5250_LOG(("GetByte: unknown escape 0x%02x in telnet-ssl stream.\n", temp));
This->state = TN5250_STREAM_STATE_NO_DATA; /* Hopefully a good recovery. */
}
break;
case TN5250_STREAM_STATE_HAVE_VERB:
TN5250_LOG(("HOST, This->status = %d %d\n", HOST, This->status));
if (This->status&HOST) {
temp = ssl_stream_host_verb(This, verb, (UCHAR) temp);
if (temp<1) {
DUMP_ERR_STACK ();
return -2;
}
/* Implement later...
This->options |= temp;
*/
} else
ssl_stream_do_verb(This, verb, (UCHAR) temp);
This->state = TN5250_STREAM_STATE_NO_DATA;
break;
case TN5250_STREAM_STATE_HAVE_SB:
if (temp == IAC)
This->state = TN5250_STREAM_STATE_HAVE_SB_IAC;
else
tn5250_buffer_append_byte(&(This->sb_buf), (UCHAR) temp);
break;
case TN5250_STREAM_STATE_HAVE_SB_IAC:
switch (temp) {
case IAC:
tn5250_buffer_append_byte(&(This->sb_buf), IAC);
/* Since the IAC code was escaped, shouldn't we be resetting the
state as in the following statement? Please verify and
uncomment if applicable. GJS 2/25/2000 */
/* This->state = TN5250_STREAM_STATE_HAVE_SB; */
break;
case SE:
if (This->status&HOST)
ssl_stream_host_sb(This, tn5250_buffer_data(&This->sb_buf),
tn5250_buffer_length(&This->sb_buf));
else
ssl_stream_sb(This, tn5250_buffer_data(&(This->sb_buf)),
tn5250_buffer_length(&(This->sb_buf)));
tn5250_buffer_free(&(This->sb_buf));
This->state = TN5250_STREAM_STATE_NO_DATA;
break;
default: /* Should never happen -- server error */
TN5250_LOG(("GetByte: huh? Got IAC SB 0x%02X.\n", temp));
This->state = TN5250_STREAM_STATE_HAVE_SB;
break;
}
break;
default:
TN5250_LOG(("GetByte: huh? Invalid state %d.\n", This->state));
TN5250_ASSERT(0);
break; /* Avoid compiler warning. */
}
} while (This->state != TN5250_STREAM_STATE_DATA);
return (int) temp;
}
/****i* lib5250/ssl_stream_write
* NAME
* ssl_stream_write
* SYNOPSIS
* ssl_stream_write (This, data, size);
* INPUTS
* Tn5250Stream * This -
* unsigned char * data -
* int size -
* DESCRIPTION
* Writes size bytes of data (pointed to by *data) to the 5250 data stream.
* This is also a temporary method to aid in the conversion process.
*****/
static void ssl_stream_write(Tn5250Stream * This, unsigned char *data, int size)
{
int r;
fd_set fdw;
while (size>0) {
r = SSL_write(This->ssl_handle, data, size);
if (r < 1) {
errnum = SSL_get_error(This->ssl_handle, r);
if ((errnum!=SSL_ERROR_WANT_READ)&&(errnum!=SSL_ERROR_WANT_WRITE)) {
}
FD_ZERO(&fdw);
FD_SET(This->sockfd, &fdw);
if (errnum==SSL_ERROR_WANT_READ)
select(This->sockfd+1, &fdw, NULL, NULL, NULL);
else
select(This->sockfd+1, NULL, &fdw, NULL, NULL);
}
else {
data += r;
size -= r;
}
}
return;
}
/****i* lib5250/ssl_stream_send_packet
* NAME
* ssl_stream_send_packet
* SYNOPSIS
* ssl_stream_send_packet (This, length, flowtype, flags, opcode, data);
* INPUTS
* Tn5250Stream * This -
* int length -
* int flowtype -
* unsigned char flags -
* unsgined char opcode -
* unsigned char * data -
* DESCRIPTION
* Send a packet, prepending a header and escaping any naturally
* occuring IAC characters.
*****/
static void ssl_stream_send_packet(Tn5250Stream * This, int length,
StreamHeader header, unsigned char *data)
{
Tn5250Buffer out_buf;
int n;
int flowtype;
unsigned char flags;
unsigned char opcode;
flowtype = header.h5250.flowtype;
flags = header.h5250.flags;
opcode = header.h5250.opcode;
length = length + 10;
/* Fixed length portion of header */
tn5250_buffer_init(&out_buf);
tn5250_buffer_append_byte(&out_buf, (UCHAR) (((short)length)>>8));
tn5250_buffer_append_byte(&out_buf, (UCHAR) (length & 0xff));
tn5250_buffer_append_byte(&out_buf, 0x12); /* Record type = General data stream (GDS) */
tn5250_buffer_append_byte(&out_buf, 0xa0);
tn5250_buffer_append_byte(&out_buf, (UCHAR)(flowtype >> 8));
tn5250_buffer_append_byte(&out_buf, (UCHAR) (flowtype & 0xff));
/* Variable length portion of header */
tn5250_buffer_append_byte(&out_buf, 4);
tn5250_buffer_append_byte(&out_buf, flags);
tn5250_buffer_append_byte(&out_buf, 0);
tn5250_buffer_append_byte(&out_buf, opcode);
tn5250_buffer_append_data(&out_buf, data, length - 10);
ssl_stream_escape(&out_buf);
tn5250_buffer_append_byte(&out_buf, IAC);
tn5250_buffer_append_byte(&out_buf, EOR);
#ifndef NDEBUG
TN5250_LOG(("SendPacket: length = %d\nSendPacket: data follows.",
tn5250_buffer_length(&out_buf)));
for (n = 0; n < tn5250_buffer_length(&out_buf); n++) {
if ((n % 16) == 0) {
TN5250_LOG(("\nSendPacket: data: "));
}
TN5250_LOG(("%02X ", tn5250_buffer_data(&out_buf)[n]));
}
TN5250_LOG(("\n"));
#endif
ssl_stream_write(This, tn5250_buffer_data(&out_buf), tn5250_buffer_length(&out_buf));
tn5250_buffer_free(&out_buf);
}
void
tn3270_ssl_stream_send_packet(Tn5250Stream * This, int length,
StreamHeader header,
unsigned char * data)
{
Tn5250Buffer out_buf;
tn5250_buffer_init(&out_buf);
if(This->streamtype == TN3270E_STREAM)
{
tn5250_buffer_append_byte(&out_buf, header.h3270.data_type);
tn5250_buffer_append_byte(&out_buf, header.h3270.request_flag);
tn5250_buffer_append_byte(&out_buf, header.h3270.response_flag);
tn5250_buffer_append_byte(&out_buf, header.h3270.sequence >> 8);
tn5250_buffer_append_byte(&out_buf, header.h3270.sequence & 0x00ff);
}
tn5250_buffer_append_data(&out_buf, data, length);
ssl_stream_escape(&out_buf);
tn5250_buffer_append_byte(&out_buf, IAC);
tn5250_buffer_append_byte(&out_buf, EOR);
ssl_stream_write(This, tn5250_buffer_data(&out_buf),
tn5250_buffer_length(&out_buf));
tn5250_buffer_free(&out_buf);
}
/****f* lib5250/ssl_stream_handle_receive
* NAME
* ssl_stream_handle_receive
* SYNOPSIS
* ret = ssl_stream_handle_receive (This);
* INPUTS
* Tn5250Stream * This -
* DESCRIPTION
* Read as much data as possible in a non-blocking fasion, form it
* into Tn5250Record structures and queue them for retrieval.
*****/
int ssl_stream_handle_receive(Tn5250Stream * This)
{
int c;
fd_set rdwait;
struct timeval tv;
/*
* note that we have to do this here, not in _get_byte, because
* we need to know that the SSL's internal buffer is empty, and
* that SSL_read is not waiting for space in the write buffer,
* before we can safely call select().
*
* Actually, not sure why we have to do this at all. Doesn't the
* work in terminal_waitevent do this already? -SCK
*
*/
if (This->msec_wait > 0) {
tv.tv_sec = This->msec_wait / 1000;
tv.tv_usec = (This->msec_wait % 1000) * 1000;
FD_ZERO(&rdwait);
FD_SET(This->sockfd, &rdwait);
select(This->sockfd+1, &rdwait, NULL, NULL, &tv);
}
/* -1 = no more data, -2 = we've been disconnected */
while ((c = ssl_stream_get_byte(This)) != -1 && c != -2) {
if (c == -END_OF_RECORD && This->current_record != NULL) {
/* End of current packet. */
#ifndef NDEBUG
if (tn5250_logfile!=NULL)
tn5250_record_dump(This->current_record);
#endif
This->records = tn5250_record_list_add(This->records, This->current_record);
This->current_record = NULL;
This->record_count++;
continue;
}
if (This->current_record == NULL) {
/* Start of new packet. */
This->current_record = tn5250_record_new();
}
tn5250_record_append_byte(This->current_record, (unsigned char) c);
}
return (c != -2);
}
/****i* lib5250/ssl_stream_escape
* NAME
* ssl_stream_escape
* SYNOPSIS
* ssl_stream_escape (in);
* INPUTS
* Tn5250Buffer * in -
* DESCRIPTION
* Escape IACs in data before sending it to the host.
*****/
static void ssl_stream_escape(Tn5250Buffer * in)
{
Tn5250Buffer out;
register unsigned char c;
int n;
tn5250_buffer_init(&out);
for (n = 0; n < tn5250_buffer_length(in); n++) {
c = tn5250_buffer_data(in)[n];
tn5250_buffer_append_byte(&out, c);
if (c == IAC)
tn5250_buffer_append_byte(&out, IAC);
}
tn5250_buffer_free(in);
memcpy(in, &out, sizeof(Tn5250Buffer));
}
/****i* lib5250/ssl_stream_passwd_cb
* NAME
* ssl_stream_passwd_cb
* SYNOPSIS
* ssl_stream_passwd_cb (buf, sizeof(buf), rwflag, userdata);
* INPUTS
* char * buf -
* int size -
* int rwflag -
* void * userdata -
* DESCRIPTION
* This is a callback function that's passed to OpenSSL. When
* OpenSSL needs a password for a Private Key file, it will call
* this function.
*****/
int ssl_stream_passwd_cb(char *buf, int size, int rwflag, Tn5250Stream *This) {
/** FIXME: There might be situations when we want to ask the user for
the passphrase rather than have it supplied by a config option? **/
strncpy(buf, This->userdata, size);
buf[size - 1] = '\0';
return(strlen(buf));
}
X509 *ssl_stream_load_cert(Tn5250Stream *This, const char *file) {
BIO *cf;
X509 *x;
if ((cf = BIO_new(BIO_s_file())) == NULL) {
DUMP_ERR_STACK();
return NULL;
}
if (BIO_read_filename(cf, file) <= 0) {
DUMP_ERR_STACK();
return NULL;
}
x = PEM_read_bio_X509_AUX(cf, NULL,
(pem_password_cb *)ssl_stream_passwd_cb, This);
BIO_free(cf);
return (x);
}
#endif /* HAVE_LIBSSL */
/* vi:set sts=3 sw=3: */
syntax highlighted by Code2HTML, v. 0.9.1