It is the 1706th of March 2020 (aka the 31st of October 2024)
You are 3.17.162.32,
pleased to meet you!
mailto:blog-at-heyrick-dot-eu
Before everything else...
Happy birthday Mom!
Simple RISC OS SSL fetcher
As explained the other day, I needed to set up my Manga reader to handle SSL connections. Thankfully, the R-Comp "SecureSockets" module came to the rescue. It's old and clunky (like pretty much anything to do with networking on RISC OS) but it works.
In this b.log article, I'm going to demonstrate how to use the module to implement your own SSL connections.
First, a simple web fetcher
I present below the code to fetch web pages from the internet. It is hardwired to fetch this page from my site. Build it with the DDE and run it from a TaskWindow...
/* Simple HTTP fetcher
To demonstrate fetching web pages.
http://www.heyrick.co.uk/blog/index.php?diary=20180312
Written by Rick Murray, Monday, 12th March 2018.
Released under the EUPL.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kernel.h"
#include "TCPIPLibs:socklib.h" // Socket functions
#include "TCPIPLibs:sys.errno.h" // Error defs
#include "TCPIPLibs:sys.socket.h" // Miscellaneous socket stuff
#include "TCPIPLibs:netinet.in.h" // sockaddr_in definition
#include "TCPIPLibs:netdb.h" // Resolver
#include "TCPIPLibs:unixlib.h" // For bcopy()
_kernel_swi_regs r;
char domain[] = "www.heyrick.co.uk";
char url[] = "/blog/index.php?diary=20180312";
char workspace[1024];
int main(void)
{
struct sockaddr_in server;
struct hostent *rb = 0;
struct servent *sp = 0;
int handle = 0;
int returncode = 0;
int amount = 0;
int zeros = 0;
// Resolve domain
rb = gethostbyname(domain);
if (rb == 0)
{
printf("Unable to resolve \"%s\".\n", domain);
exit(0);
}
// Create a handle
handle = socket(AF_INET, SOCK_STREAM, 0);
if (handle == -1)
{
printf("Unable to create a handle.\n");
exit(0);
}
// Set up the IP address block
bzero((char *)&server, sizeof(server));
bcopy(rb->h_addr, (char *)&server.sin_addr, rb->h_length);
server.sin_family = AF_INET;
sp = getservbyname("http", "tcp");
server.sin_port = sp->s_port;
// Connect to host
returncode = connect(handle, (struct sockaddr *)&server, sizeof(server));
if (returncode == -1)
{
socketclose(handle);
printf("Unable to connect to \"%s\".\n", domain);
exit(0);
}
// Mark the socket as non-blocking (&8004667E = FIONBIO)
returncode = 1;
socketioctl(handle, 0x8004667E, &returncode);
// Now we're connected, so say what we want
sprintf(workspace, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n", url, domain);
returncode = socketwrite(handle, workspace, strlen(workspace));
if (returncode == -1)
{
socketclose(handle);
printf("Error while sending data to host.\n");
exit(0);
}
// Now receive data
do
{
// Read some data
amount = recv(handle, workspace, 1024, 0x80);
// Error?
if ( (amount < 0) && (errno != EWOULDBLOCK) )
{
zeros = 0;
socketclose(handle);
printf("Error %d while reading data from host.\n", errno);
exit(0);
}
// No data?
if ( amount == 0 )
{
zeros++;
if ( zeros > 255 )
{
// 255 times we received nothing, connection probably ended.
socketclose(handle);
exit(1);
}
}
// Some data
if ( amount > 0 )
{
// We have something to read, so...
zeros = 0;
for (int recurse = 0; recurse < amount; recurse++)
{
int byte = workspace[recurse];
// Filter control characters other than linefeed
if ( (byte < 32) && (byte != 10 ) )
byte = 0;
// Output filtered byte
if (byte != 0)
putchar(byte);
}
} // end of "if ( amount > 0 )"
} while ( 1 );
return 0; // never comes here
}
This is a fairly straightforward fetcher. The only weird bit is the use of a counter to timeout the connection. This is because a non-blocking socket does not close itself when the connection has finished, it reports EWOULDBLOCK. So you're supposed to use select(), right? Only select() kept telling me the connection was still active.
I'm probably doing it wrong.
Here's the code I used. This goes between the do and the call to recv():
// Wait for some data
do
{
FD_ZERO(&readset);
FD_SET(handle, &readset);
returncode = select( (handle + 1), &readset, NULL, NULL, NULL );
printf("returncode = %d, errno = %d\n", returncode, errno);
} while ( returncode == -1 );
// Did the other side close the connection?
if ( returncode == 0 )
{
// Yes!
socketclose(handle);
printf("\n** END **\n");
exit(0);
}
Now enter SSL
The way the SecureSockets module has been implemented leads to a rather simple utilisation. One needs only to establish the socket connection as normal, then direct the SecureSockets module to establish an SSL connection using that socket connection, and finally to send and receive data via the SecureSockets module.
In essence, creating a connection within a connection.
/* Simple HTTPS fetcher
To demonstrate fetching web pages using an encrypted connection.
https://www.heyrick.co.uk/blog/index.php?diary=20180312
Written by Rick Murray, Monday, 12th March 2018.
Released under the EUPL.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kernel.h"
#include "TCPIPLibs:socklib.h" // Socket functions
#include "TCPIPLibs:sys.errno.h" // Error defs
#include "TCPIPLibs:sys.socket.h" // Miscellaneous socket stuff
#include "TCPIPLibs:netinet.in.h" // sockaddr_in definition
#include "TCPIPLibs:netdb.h" // Resolver
#include "TCPIPLibs:unixlib.h" // For bcopy()
#define XSecure_Connect 0x74280
#define XSecure_Read 0x74282
#define XSecure_Peek 0x74283
#define XSecure_Write 0x74284
#define XSecure_GetError 0x74285
#define XSecure_Disconnect 0x74286
#define XSecure_State 0x74292
#define XSecure_PollConnect 0x74293
static int socketwritessl(int, const void *, unsigned int);
static void socketclosessl(int);
static int recvssl(int, char *, int, int);
_kernel_swi_regs r;
char domain[] = "www.heyrick.co.uk";
char url[] = "/blog/index.php?diary=20180312";
char workspace[1024];
int main(void)
{
struct sockaddr_in server;
struct hostent *rb = 0;
int handle = 0;
int returncode = 0;
int amount = 0;
int sslhandle = 0;
int sslcode = 0;
_kernel_oserror *err;
// Resolve domain
rb = gethostbyname(domain);
if (rb == 0)
{
printf("Unable to resolve \"%s\".\n", domain);
exit(0);
}
// Create a handle
handle = socket(AF_INET, SOCK_STREAM, 0);
if (handle == -1)
{
printf("Unable to create a handle.\n");
exit(0);
}
// Set up the IP address block
bzero((char *)&server, sizeof(server));
bcopy(rb->h_addr, (char *)&server.sin_addr, rb->h_length);
server.sin_family = AF_INET;
server.sin_port = htons(443); // SSL is on port 443
// Connect to host
returncode = connect(handle, (struct sockaddr *)&server, sizeof(server));
if (returncode == -1)
{
socketclose(handle);
printf("Unable to connect to \"%s\".\n", domain);
exit(0);
}
// Mark the socket as non-blocking (&8004667E = FIONBIO)
returncode = 1;
socketioctl(handle, 0x8004667E, &returncode);
// Initiate SSL connection
r.r[0] = 4; // SSLv3 client, can downgrade to SSLv2
r.r[1] = handle;
err = _kernel_swi(XSecure_Connect, &r, &r);
if ( err )
{
socketclose(handle);
printf("Error establishing SSL connection: %s\n", err->errmess);
exit(0);
}
sslhandle = r.r[0];
if ( sslhandle == 0 )
{
socketclose(handle);
printf("SSL connection failed.\n");
exit(0);
}
// Poll while awaiting SSL connection
do
{
r.r[0] = sslhandle;
_kernel_swi(XSecure_PollConnect, &r, &r);
sslcode = r.r[0];
} while ( sslcode != 1 );
// Now we're connected, so say what we want
sprintf(workspace, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n", url, domain);
returncode = socketwritessl(sslhandle, workspace, strlen(workspace));
if (returncode == -1)
{
socketclosessl(sslhandle);
socketclose(handle);
printf("Error while sending data to host.\n");
exit(0);
}
// Now receive data
while ( sslcode != 6 )
{
// Read some data
amount = recvssl(sslhandle, workspace, 1024, 0x80);
// Error?
if ( amount <= 0 )
{
// Less than or equal to zero bytes received - possible error?
r.r[0] = sslhandle;
r.r[1] = amount;
_kernel_swi(XSecure_GetError, &r, &r);
sslcode = r.r[0];
}
else
{
// More than zero bytes, we have sone data
for (int recurse = 0; recurse < amount; recurse++)
{
int byte = workspace[recurse];
// Filter control characters other than linefeed
if ( (byte < 32) && (byte != 10 ) )
byte = 0;
// Output filtered byte
if (byte != 0)
putchar(byte);
}
} // end of "if ( amount > 0 )"
}; // end of "while (sslcode != 5)"
// We come here when all data has been received
socketclosessl(sslhandle);
socketclose(handle);
return 0;
}
static int socketwritessl(int sslhdl, const void *buf, unsigned int len)
{
r.r[0] = sslhdl;
r.r[1] = (int)buf;
r.r[2] = len;
_kernel_swi(XSecure_Write, &r, &r);
return r.r[0];
}
static void socketclosessl(int sslhdl)
{
r.r[0] = sslhdl;
_kernel_swi(XSecure_Disconnect, &r, &r);
return;
}
static int recvssl(int sslhdl, char *buf, int blen, int flags)
{
// we ignore flags
r.r[0] = sslhdl;
r.r[1] = (int)buf;
r.r[2] = blen;
_kernel_swi(XSecure_Read, &r, &r);
return r.r[0];
}
As can be seen from the above, once the socket is connected and set to non-blocking, a call is made to Secure_Connect to begin the process, and then we just sit on Secure_PollConnect until it is done.
From this point on, we can use Secure_Read and Secure_Write to send and receive data. Sometimes the SSL system will return an error status (perhaps if the data hasn't been fully received for decoding) so we must check by calling Secure_GetError to see what went wrong. If the error response is '6', this means the connection has finished and all data has been received; though I notice that the response is '5' in !Manga - I don't know if polling the Wimp changes the behaviour or what, but it is perhaps worth being aware of this. A simple log to DADebug will indicate what response is being received, should you need to test it.
The three functions at the end basically mimic socketwrite(), socketclose(), and recv() but using the SSL method instead of plain socket actions.
As you can see, adding support for SSL is not too complex. Therefore, hopefully, this article has demonstrated how to implement SSL in your own programs, which may be useful now that Google is strongarming everybody to switch to SSL by yesterday...
Finally, here's the code and prebuilt executables:
Well. It's here now. The man came at half past eleven. The old meter was disconnected in a matter of minutes, and the Linky installed in its place. A special box was plugged in, I presume to set up the firmware with the appropriate billing information. The change took about twenty minutes, and the man was very particular to check that everything was screwed in place extremely tightly - I'm guessing Enerdis (formerly EDF) don't want their smart meter rollout blighted by too many problems.
I asked if I could keep the old meter (a very reliable thing that predates me) and was told that it isn't possible. The poor thing, that kept on going strong after countless lightning strikes, will probably be gutted, stripped, melted down, and turned into part of a bonnet for a bloody Dacia...
Some photos. First up, the original meter sporting a nice selection of cobwebs.
Here's the installer hooking up the Linky:
And, finally, the puke green Linky. Seriously, this photo doesn't do justice to the bizarre colour. The little light at the top blinks at a speed relative to how much energy is being consumed. The screen, by default, is off. Pressing a button will show the current reading and contract type (base meaning there's no weird stuff like special rates or off-peak). The "0 kWh" currently reads "2 kWh" as I write this.
Pressing buttons will show other information such as daily maximal power consumption rated in volt-amps, or the current consumption in volt-amps. Since the meter needs to convert volts and amps to watts, it's a shame it can't display the current consumption in W or kW (I have a plug that can manage at least that much!) as I would imagine people could better equate a wattage rating (given most domestic devices are measured in watts) than some sort of obscure value such as "8VA". Given that converting VA to W requires understanding the power factor and the difference between inductive (pf=0) and resistive (pf=1) loads, it can get pretty hairy pretty quickly...
Here is, apparently, a formula for working out the correct power factor for a three phase system by using a metered reading in kilowatts (P), a line-to-neutral value (VL-N) in volts, and the current in amps (I). I say apparently, because if I just strung together some random Asian characters, it would make about as much sense to me... PF = |cos φ| = 1000 × P(kW) / (3 × VL-N(V) × I(A))
Yeah... didn't we invent computers so we didn't have to do maths like that?
Here it is then, here's Linky:
Your comments:
Please note that while I check this page every so often, I am not able to control what users write; therefore I disclaim all liability for unpleasant and/or infringing and/or defamatory material. Undesired content will be removed as soon as it is noticed. By leaving a comment, you agree not to post material that is illegal or in bad taste, and you should be aware that the time and your IP address are both recorded, should it be necessary to find out who you are. Oh, and don't bother trying to inline HTML. I'm not that stupid! ☺ ADDING COMMENTS DOES NOT WORK IF READING TRANSLATED VERSIONS.
You can now follow comment additions with the comment RSS feed. This is distinct from the b.log RSS feed, so you can subscribe to one or both as you wish.
Jeff Doggett, 14th March 2018, 15:24
bzero and bcopy are old non-standard functions. You should be using the Ansi standard memset and memcpy.
This web page is licenced for your personal, private, non-commercial use only. No automated processing by advertising systems is permitted.
RIPA notice: No consent is given for interception of page transmission.