Initial commit of libwsclient

Only got the handshake and basic text frame sending done.
master
Payden Sutherland 12 years ago
commit d01f0a9e13

42
.gitignore vendored

@ -0,0 +1,42 @@
# http://www.gnu.org/software/automake
Makefile.in
Makefile
# http://www.gnu.org/software/autoconf
*.la
/autom4te.cache
/aclocal.m4
/compile
/configure
/depcomp
/install-sh
/missing
test
*~
.*~
.cproject
.project
.settings
*.o
*.lo
.*.swp
.libs
.deps
*.html
/autom4te.cache
/stamp-h1
Makefile
/config.status
/config.log
/config.h
/aclocal.m4
Makefile.in
/config/
/configure
*~
m4/*.m4
/build*/
ltmain.sh
libtool
libwebsock.la

@ -0,0 +1 @@
/usr/share/automake-1.11/COPYING

@ -0,0 +1 @@
/usr/share/automake-1.11/INSTALL

@ -0,0 +1,5 @@
lib_LTLIBRARIES=libwsclient.la
libwsclient_la_SOURCES = wsclient.c base64.c sha1.c
library_includedir=$(includedir)/wsclient
library_include_HEADERS = wsclient.h
ACLOCAL_AMFLAGS = -I m4

@ -0,0 +1,5 @@
#! /bin/sh
libtoolize
aclocal \
&& automake --add-missing \
&& autoconf

@ -0,0 +1,215 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/**
* characters used for Base64 encoding
*/
const char *BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* encode three bytes using base64 (RFC 3548)
*
* @param triple three bytes that should be encoded
* @param result buffer of four characters where the result is stored
*/
void _base64_encode_triple(unsigned char triple[3], char result[4])
{
int tripleValue, i;
tripleValue = triple[0];
tripleValue *= 256;
tripleValue += triple[1];
tripleValue *= 256;
tripleValue += triple[2];
for (i=0; i<4; i++)
{
result[3-i] = BASE64_CHARS[tripleValue%64];
tripleValue /= 64;
}
}
/**
* encode an array of bytes using Base64 (RFC 3548)
*
* @param source the source buffer
* @param sourcelen the length of the source buffer
* @param target the target buffer
* @param targetlen the length of the target buffer
* @return 1 on success, 0 otherwise
*/
int base64_encode(unsigned char *source, size_t sourcelen, char *target, size_t targetlen)
{
/* check if the result will fit in the target buffer */
if ((sourcelen+2)/3*4 > targetlen-1)
return 0;
/* encode all full triples */
while (sourcelen >= 3)
{
_base64_encode_triple(source, target);
sourcelen -= 3;
source += 3;
target += 4;
}
/* encode the last one or two characters */
if (sourcelen > 0)
{
unsigned char temp[3];
memset(temp, 0, sizeof(temp));
memcpy(temp, source, sourcelen);
_base64_encode_triple(temp, target);
target[3] = '=';
if (sourcelen == 1)
target[2] = '=';
target += 4;
}
/* terminate the string */
target[0] = 0;
return 1;
}
/**
* determine the value of a base64 encoding character
*
* @param base64char the character of which the value is searched
* @return the value in case of success (0-63), -1 on failure
*/
int _base64_char_value(char base64char)
{
if (base64char >= 'A' && base64char <= 'Z')
return base64char-'A';
if (base64char >= 'a' && base64char <= 'z')
return base64char-'a'+26;
if (base64char >= '0' && base64char <= '9')
return base64char-'0'+2*26;
if (base64char == '+')
return 2*26+10;
if (base64char == '/')
return 2*26+11;
return -1;
}
/**
* decode a 4 char base64 encoded byte triple
*
* @param quadruple the 4 characters that should be decoded
* @param result the decoded data
* @return lenth of the result (1, 2 or 3), 0 on failure
*/
int _base64_decode_triple(char quadruple[4], unsigned char *result)
{
int i, triple_value, bytes_to_decode = 3, only_equals_yet = 1;
int char_value[4];
for (i=0; i<4; i++)
char_value[i] = _base64_char_value(quadruple[i]);
/* check if the characters are valid */
for (i=3; i>=0; i--)
{
if (char_value[i]<0)
{
if (only_equals_yet && quadruple[i]=='=')
{
/* we will ignore this character anyway, make it something
* that does not break our calculations */
char_value[i]=0;
bytes_to_decode--;
continue;
}
return 0;
}
/* after we got a real character, no other '=' are allowed anymore */
only_equals_yet = 0;
}
/* if we got "====" as input, bytes_to_decode is -1 */
if (bytes_to_decode < 0)
bytes_to_decode = 0;
/* make one big value out of the partial values */
triple_value = char_value[0];
triple_value *= 64;
triple_value += char_value[1];
triple_value *= 64;
triple_value += char_value[2];
triple_value *= 64;
triple_value += char_value[3];
/* break the big value into bytes */
for (i=bytes_to_decode; i<3; i++)
triple_value /= 256;
for (i=bytes_to_decode-1; i>=0; i--)
{
result[i] = triple_value%256;
triple_value /= 256;
}
return bytes_to_decode;
}
/**
* decode base64 encoded data
*
* @param source the encoded data (zero terminated)
* @param target pointer to the target buffer
* @param targetlen length of the target buffer
* @return length of converted data on success, -1 otherwise
*/
size_t base64_decode(char *source, unsigned char *target, size_t targetlen)
{
char *src, *tmpptr;
char quadruple[4], tmpresult[3];
int i, tmplen = 3;
size_t converted = 0;
/* concatinate '===' to the source to handle unpadded base64 data */
src = (char *)malloc(strlen(source)+5);
if (src == NULL)
return -1;
strcpy(src, source);
strcat(src, "====");
tmpptr = src;
/* convert as long as we get a full result */
while (tmplen == 3)
{
/* get 4 characters to convert */
for (i=0; i<4; i++)
{
/* skip invalid characters - we won't reach the end */
while (*tmpptr != '=' && _base64_char_value(*tmpptr)<0)
tmpptr++;
quadruple[i] = *(tmpptr++);
}
/* convert the characters */
tmplen = _base64_decode_triple(quadruple, tmpresult);
/* check if the fit in the result buffer */
if (targetlen < tmplen)
{
free(src);
return -1;
}
/* put the partial result in the result buffer */
memcpy(target, tmpresult, tmplen);
target += tmplen;
targetlen -= tmplen;
converted += tmplen;
}
free(src);
return converted;
}

1
config.guess vendored

@ -0,0 +1 @@
/usr/share/automake-1.11/config.guess

@ -0,0 +1,97 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
to 0 otherwise. */
#undef HAVE_MALLOC
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the `memset' function. */
#undef HAVE_MEMSET
/* Define to 1 if you have the <netdb.h> header file. */
#undef HAVE_NETDB_H
/* Define to 1 if your system has a GNU libc compatible `realloc' function,
and to 0 otherwise. */
#undef HAVE_REALLOC
/* Define to 1 if you have the `socket' function. */
#undef HAVE_SOCKET
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the `strchr' function. */
#undef HAVE_STRCHR
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the `strstr' function. */
#undef HAVE_STRSTR
/* Define to 1 if you have the <sys/socket.h> header file. */
#undef HAVE_SYS_SOCKET_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#undef LT_OBJDIR
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Version number of package */
#undef VERSION
/* Define to rpl_malloc if the replacement function should be used. */
#undef malloc
/* Define to rpl_realloc if the replacement function should be used. */
#undef realloc
/* Define to `unsigned int' if <sys/types.h> does not define. */
#undef size_t

1
config.sub vendored

@ -0,0 +1 @@
/usr/share/automake-1.11/config.sub

@ -0,0 +1,28 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.60])
AC_INIT([libwsclient], [1.0.1], [payden@paydensutherland.com])
AM_INIT_AUTOMAKE
LT_INIT([disable-static])
AC_CONFIG_SRCDIR([wsclient.c])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
AC_CHECK_HEADERS([netdb.h stdlib.h string.h sys/socket.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T
# Checks for library functions.
AC_FUNC_MALLOC
AC_FUNC_REALLOC
AC_CHECK_FUNCS([memset socket strstr strchr])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

371
sha1.c

@ -0,0 +1,371 @@
/*
* sha1.c
*
* Copyright (C) 1998, 2009
* Paul E. Jones <paulej@packetizer.com>
* All Rights Reserved
*
*****************************************************************************
* $Id: sha1.c 12 2009-06-22 19:34:25Z paulej $
*****************************************************************************
*
* Description:
* This file implements the Secure Hashing Standard as defined
* in FIPS PUB 180-1 published April 17, 1995.
*
* The Secure Hashing Standard, which uses the Secure Hashing
* Algorithm (SHA), produces a 160-bit message digest for a
* given data stream. In theory, it is highly improbable that
* two messages will produce the same message digest. Therefore,
* this algorithm can serve as a means of providing a "fingerprint"
* for a message.
*
* Portability Issues:
* SHA-1 is defined in terms of 32-bit "words". This code was
* written with the expectation that the processor has at least
* a 32-bit machine word size. If the machine word size is larger,
* the code should still function properly. One caveat to that
* is that the input functions taking characters and character
* arrays assume that only 8 bits of information are stored in each
* character.
*
* Caveats:
* SHA-1 is designed to work with messages less than 2^64 bits
* long. Although SHA-1 allows a message digest to be generated for
* messages of any number of bits less than 2^64, this
* implementation only works with messages with a length that is a
* multiple of the size of an 8-bit character.
*
*/
#include "sha1.h"
/*
* Define the circular shift macro
*/
#define SHA1CircularShift(bits,word) \
((((word) << (bits)) & 0xFFFFFFFF) | \
((word) >> (32-(bits))))
/* Function prototypes */
void SHA1ProcessMessageBlock(SHA1Context *);
void SHA1PadMessage(SHA1Context *);
/*
* SHA1Reset
*
* Description:
* This function will initialize the SHA1Context in preparation
* for computing a new message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* Nothing.
*
* Comments:
*
*/
void SHA1Reset(SHA1Context *context)
{
context->Length_Low = 0;
context->Length_High = 0;
context->Message_Block_Index = 0;
context->Message_Digest[0] = 0x67452301;
context->Message_Digest[1] = 0xEFCDAB89;
context->Message_Digest[2] = 0x98BADCFE;
context->Message_Digest[3] = 0x10325476;
context->Message_Digest[4] = 0xC3D2E1F0;
context->Computed = 0;
context->Corrupted = 0;
}
/*
* SHA1Result
*
* Description:
* This function will return the 160-bit message digest into the
* Message_Digest array within the SHA1Context provided
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA-1 hash.
*
* Returns:
* 1 if successful, 0 if it failed.
*
* Comments:
*
*/
int SHA1Result(SHA1Context *context)
{
if (context->Corrupted)
{
return 0;
}
if (!context->Computed)
{
SHA1PadMessage(context);
context->Computed = 1;
}
return 1;
}
/*
* SHA1Input
*
* Description:
* This function accepts an array of octets as the next portion of
* the message.
*
* Parameters:
* context: [in/out]
* The SHA-1 context to update
* message_array: [in]
* An array of characters representing the next portion of the
* message.
* length: [in]
* The length of the message in message_array
*
* Returns:
* Nothing.
*
* Comments:
*
*/
void SHA1Input( SHA1Context *context,
const unsigned char *message_array,
unsigned length)
{
if (!length)
{
return;
}
if (context->Computed || context->Corrupted)
{
context->Corrupted = 1;
return;
}
while(length-- && !context->Corrupted)
{
context->Message_Block[context->Message_Block_Index++] =
(*message_array & 0xFF);
context->Length_Low += 8;
/* Force it to 32 bits */
context->Length_Low &= 0xFFFFFFFF;
if (context->Length_Low == 0)
{
context->Length_High++;
/* Force it to 32 bits */
context->Length_High &= 0xFFFFFFFF;
if (context->Length_High == 0)
{
/* Message is too long */
context->Corrupted = 1;
}
}
if (context->Message_Block_Index == 64)
{
SHA1ProcessMessageBlock(context);
}
message_array++;
}
}
/*
* SHA1ProcessMessageBlock
*
* Description:
* This function will process the next 512 bits of the message
* stored in the Message_Block array.
*
* Parameters:
* None.
*
* Returns:
* Nothing.
*
* Comments:
* Many of the variable names in the SHAContext, especially the
* single character names, were used because those were the names
* used in the publication.
*
*
*/
void SHA1ProcessMessageBlock(SHA1Context *context)
{
const unsigned K[] = /* Constants defined in SHA-1 */
{
0x5A827999,
0x6ED9EBA1,
0x8F1BBCDC,
0xCA62C1D6
};
int t; /* Loop counter */
unsigned temp; /* Temporary word value */
unsigned W[80]; /* Word sequence */
unsigned A, B, C, D, E; /* Word buffers */
/*
* Initialize the first 16 words in the array W
*/
for(t = 0; t < 16; t++)
{
W[t] = ((unsigned) context->Message_Block[t * 4]) << 24;
W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16;
W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8;
W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]);
}
for(t = 16; t < 80; t++)
{
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
}
A = context->Message_Digest[0];
B = context->Message_Digest[1];
C = context->Message_Digest[2];
D = context->Message_Digest[3];
E = context->Message_Digest[4];
for(t = 0; t < 20; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | ((~B) & D)) + E + W[t] + K[0];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 20; t < 40; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 40; t < 60; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 60; t < 80; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
context->Message_Digest[0] =
(context->Message_Digest[0] + A) & 0xFFFFFFFF;
context->Message_Digest[1] =
(context->Message_Digest[1] + B) & 0xFFFFFFFF;
context->Message_Digest[2] =
(context->Message_Digest[2] + C) & 0xFFFFFFFF;
context->Message_Digest[3] =
(context->Message_Digest[3] + D) & 0xFFFFFFFF;
context->Message_Digest[4] =
(context->Message_Digest[4] + E) & 0xFFFFFFFF;
context->Message_Block_Index = 0;
}
/*
* SHA1PadMessage
*
* Description:
* According to the standard, the message must be padded to an even
* 512 bits. The first padding bit must be a '1'. The last 64
* bits represent the length of the original message. All bits in
* between should be 0. This function will pad the message
* according to those rules by filling the Message_Block array
* accordingly. It will also call SHA1ProcessMessageBlock()
* appropriately. When it returns, it can be assumed that the
* message digest has been computed.
*
* Parameters:
* context: [in/out]
* The context to pad
*
* Returns:
* Nothing.
*
* Comments:
*
*/
void SHA1PadMessage(SHA1Context *context)
{
/*
* Check to see if the current message block is too small to hold
* the initial padding bits and length. If so, we will pad the
* block, process it, and then continue padding into a second
* block.
*/
if (context->Message_Block_Index > 55)
{
context->Message_Block[context->Message_Block_Index++] = 0x80;
while(context->Message_Block_Index < 64)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
SHA1ProcessMessageBlock(context);
while(context->Message_Block_Index < 56)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
}
else
{
context->Message_Block[context->Message_Block_Index++] = 0x80;
while(context->Message_Block_Index < 56)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
}
/*
* Store the message length as the last 8 octets
*/
context->Message_Block[56] = (context->Length_High >> 24) & 0xFF;
context->Message_Block[57] = (context->Length_High >> 16) & 0xFF;
context->Message_Block[58] = (context->Length_High >> 8) & 0xFF;
context->Message_Block[59] = (context->Length_High) & 0xFF;
context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF;
context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF;
context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF;
context->Message_Block[63] = (context->Length_Low) & 0xFF;
SHA1ProcessMessageBlock(context);
}

@ -0,0 +1,54 @@
/*
* sha1.h
*
* Copyright (C) 1998, 2009
* Paul E. Jones <paulej@packetizer.com>
* All Rights Reserved
*
*****************************************************************************
* $Id: sha1.h 12 2009-06-22 19:34:25Z paulej $
*****************************************************************************
*
* Description:
* This class implements the Secure Hashing Standard as defined
* in FIPS PUB 180-1 published April 17, 1995.
*
* Many of the variable names in the SHA1Context, especially the
* single character names, were used because those were the names
* used in the publication.
*
* Please read the file sha1.c for more information.
*
*/
#ifndef _SHA1_H_
#define _SHA1_H_
/*
* This structure will hold context information for the hashing
* operation
*/
typedef struct SHA1Context
{
unsigned Message_Digest[5]; /* Message Digest (output) */
unsigned Length_Low; /* Message length in bits */
unsigned Length_High; /* Message length in bits */
unsigned char Message_Block[64]; /* 512-bit message blocks */
int Message_Block_Index; /* Index into message block array */
int Computed; /* Is the digest computed? */
int Corrupted; /* Is the message digest corruped? */
} SHA1Context;
/*
* Function Prototypes
*/
void SHA1Reset(SHA1Context *);
int SHA1Result(SHA1Context *);
void SHA1Input( SHA1Context *,
const unsigned char *,
unsigned);
#endif

@ -0,0 +1,29 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wsclient/wsclient.h>
int onopen(void) {
fprintf(stderr, "Connection opened.\n");
return 0;
}
int onmessage(char *msg, int64_t length) {
fprintf(stderr, "Received message (%ull): %s\n", length, msg);
return 0;
}
int main(int argc, char **argv) {
wsclient *client = libwsclient_new("ws://localhost:3333/mtgox");
if(!client) {
fprintf(stderr, "Unable to initialize new WS client.\n");
exit(1);
}
client->onopen = &onopen;
client->onmessage = &onmessage;
libwsclient_send(client, "Testing");
libwsclient_run(client);
return 0;
}

@ -0,0 +1,300 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "wsclient.h"
#include "sha1.h"
int libwsclient_run(wsclient *c) {
}
int libwsclient_open_connection(const char *host, const char *port) {
struct addrinfo hints, *servinfo, *p;
int rv, sockfd;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if((rv = getaddrinfo(host, port, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return -1;
}
for(p = servinfo; p != NULL; p = p->ai_next) {
if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("socket");
continue;
}
if(connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("connect");
continue;
}
break;
}
if(p == NULL) {
fprintf(stderr, "Failed to connect.\n");
freeaddrinfo(servinfo);
return -1;
}
return sockfd;
}
wsclient *libwsclient_new(const char *URI) {
SHA1Context shactx;
const char *UUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
char pre_encode[256];
char sha1bytes[20];
char expected_base64[512];
char request_headers[1024];
char websocket_key[256];
char key_nonce[16];
char scheme[10];
char host[255];
char request_host[255];
char port[10];
char path[255];
char recv_buf[1024];
char *URI_copy = NULL, *p = NULL, *rcv = NULL, *tok = NULL;
int i, z, sockfd, n, flags = 0, headers_space = 1024;
wsclient *client = (wsclient *)malloc(sizeof(wsclient));
if(!client) {
fprintf(stderr, "Unable to allocate memory in libwsclient_new.\n");
exit(1);
}
memset(client, 0, sizeof(wsclient));
URI_copy = (char *)malloc(strlen(URI)+1);
if(!URI_copy) {
fprintf(stderr, "Unable to allocate memory in libwsclient_new.\n");
exit(2);
}
memset(URI_copy, 0, strlen(URI)+1);
strncpy(URI_copy, URI, strlen(URI));
p = strstr(URI_copy, "://");
if(p == NULL) {
fprintf(stderr, "Malformed or missing scheme for URI.\n");
exit(3);
}
strncpy(scheme, URI_copy, p-URI_copy);
scheme[p-URI_copy] = '\0';
if(strcmp(scheme, "ws") != 0 && strcmp(scheme, "wss") != 0) {
fprintf(stderr, "Invalid scheme for URI: %s\n", scheme);
exit(4);
}
for(i=p-URI_copy+3,z=0;*(URI_copy+i) != '/' && *(URI_copy+i) != ':' && *(URI_copy+i) != '\0';i++,z++) {
host[z] = *(URI_copy+i);
}
host[z] = '\0';
if(*(URI_copy+i) == '\0') {
//end of URI request path will be /
strncpy(path, "/", 1);
} else {
if(*(URI_copy+i) != ':') {
if(strcmp(scheme, "ws") == 0) {
strncpy(port, "80", 9);
} else {
strncpy(port, "443", 9);
client->flags |= CLIENT_IS_SSL;
}
} else {
i++;
p = strchr(URI_copy+i, '/');
strncpy(port, URI_copy+i, (p - (URI_copy+i)));
port[p-(URI_copy+i)] = '\0';
i += p-(URI_copy+i);
}
}
strncpy(path, URI_copy+i, 254);
free(URI_copy);
sockfd = libwsclient_open_connection(host, port);
if(sockfd == -1) {
fprintf(stderr, "Error opening socket.\n");
exit(5);
}
client->sockfd = sockfd;
//perform handshake
//generate nonce
srand(time(NULL));
for(z=0;z<16;z++) {
key_nonce[z] = rand() & 0xff;
}
base64_encode(key_nonce, 16, websocket_key, 256);
memset(request_headers, 0, 1024);
if(strcmp(port, "80") != 0) {
snprintf(request_host, 256, "%s:%s", host, port);
} else {
snprintf(request_host, 256, "%s", host);
}
snprintf(request_headers, 1024, "GET %s HTTP/1.1\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nHost: %s\r\nSec-WebSocket-Key: %s\r\nSec-WebSocket-Version: 13\r\n\r\n", path, request_host, websocket_key);
n = send(client->sockfd, request_headers, strlen(request_headers), 0);
z = 0;
memset(recv_buf, 0, 1024);
do {
n = recv(client->sockfd, recv_buf + z, 1023 - z, 0);
z += n;
} while((z < 4 || strcmp(recv_buf + z - 4, "\r\n\r\n") != 0) && n > 0);
//parse recv_buf for response headers and assure Accept matches expected value
rcv = (char *)malloc(strlen(recv_buf)+1);
if(!rcv) {
fprintf(stderr, "Unable to allocate memory in libwsclient_new.\n");
exit(6);
}
memset(rcv, 0, strlen(recv_buf)+1);
strncpy(rcv, recv_buf, strlen(recv_buf));
memset(pre_encode, 0, 256);
snprintf(pre_encode, 256, "%s%s", websocket_key, UUID);
SHA1Reset(&shactx);
SHA1Input(&shactx, pre_encode, strlen(pre_encode));
SHA1Result(&shactx);
memset(pre_encode, 0, 256);
snprintf(pre_encode, 256, "%08x%08x%08x%08x%08x", shactx.Message_Digest[0], shactx.Message_Digest[1], shactx.Message_Digest[2], shactx.Message_Digest[3], shactx.Message_Digest[4]);
for(z = 0; z < (strlen(pre_encode)/2);z++)
sscanf(pre_encode+(z*2), "%02hhx", sha1bytes+z);
memset(expected_base64, 0, 512);
base64_encode(sha1bytes, 20, expected_base64, 512);
for(tok = strtok(rcv, "\r\n"); tok != NULL; tok = strtok(NULL, "\r\n")) {
if(*tok == 'H' && *(tok+1) == 'T' && *(tok+2) == 'T' && *(tok+3) == 'P') {
p = strchr(tok, ' ');
p = strchr(p+1, ' ');
*p = '\0';
if(strcmp(tok, "HTTP/1.1 101") != 0 && strcmp(tok, "HTTP/1.0 101") != 0) {
fprintf(stderr, "Invalid HTTP version or invalid HTTP status from server: %s\n", tok);
exit(7);
}
flags |= REQUEST_VALID_STATUS;
} else {
p = strchr(tok, ' ');
*p = '\0';
if(strcmp(tok, "Upgrade:") == 0) {
if(stricmp(p+1, "websocket") == 0) {
flags |= REQUEST_HAS_UPGRADE;
}
}
if(strcmp(tok, "Connection:") == 0) {
if(stricmp(p+1, "upgrade") == 0) {
flags |= REQUEST_HAS_CONNECTION;
}
}
if(strcmp(tok, "Sec-WebSocket-Accept:") == 0) {
if(strcmp(p+1, expected_base64) == 0) {
flags |= REQUEST_VALID_ACCEPT;
}
}
}
}
if(!flags & REQUEST_HAS_UPGRADE) {
fprintf(stderr, "Response from server did not include Upgrade header, failing.\n");
exit(8);
}
if(!flags & REQUEST_HAS_CONNECTION) {
fprintf(stderr, "Response from server did not include Connection header, failing.\n");
exit(9);
}
if(!flags & REQUEST_VALID_ACCEPT) {
fprintf(stderr, "Server did not send valid Sec-WebSocket-Accept header, failing.\n");
exit(10);
}
return client;
}
//somewhat hackish stricmp
int stricmp(const char *s1, const char *s2) {
register unsigned char c1, c2;
register unsigned char flipbit = ~(1 << 5);
do {
c1 = (unsigned char)*s1++ & flipbit;
c2 = (unsigned char)*s2++ & flipbit;
if(c1 == '\0')
return c1 - c2;
} while(c1 == c2);
return c1 - c2;
}
int libwsclient_send(wsclient *client, char *strdata) {
if(strdata == NULL) {
fprintf(stderr, "Will not send empty message.\n");
return -1;
}
int sockfd = client->sockfd;
struct timeval tv;
unsigned char mask[4];
unsigned int mask_int;
unsigned long long payload_len;
unsigned char finNopcode;
unsigned int payload_len_small;
unsigned int payload_offset = 6;
unsigned int len_size;
unsigned long long be_payload_len;
unsigned int sent = 0;
int i;
unsigned int frame_size;
char *data;
gettimeofday(&tv);
srand(tv.tv_usec * tv.tv_sec);
mask_int = rand();
memcpy(mask, &mask_int, 4);
payload_len = strlen(strdata);
finNopcode = 0x81; //FIN and text opcode.
if(payload_len <= 125) {
frame_size = 6 + payload_len;
data = (void *)malloc(frame_size);
payload_len_small = payload_len;
} else if(payload_len > 125 && payload_len <= 0xffff) {
frame_size = 8 + payload_len;
data = (void *)malloc(frame_size);
payload_len_small = 126;
payload_offset += 2;
} else if(payload_len > 0xffff && payload_len <= 0xffffffffffffffffLL) {
frame_size = 14 + payload_len;
data = (void *)malloc(frame_size);
payload_len_small = 127;
payload_offset += 8;
} else {
fprintf(stderr, "Whoa man. What are you trying to send?\n");
return -1;
}
memset(data, 0, frame_size);
payload_len_small |= 0x80;
memcpy(data, &finNopcode, 1);
memcpy(data+1, &payload_len_small, 1); //mask bit om, 7 bit payload len
if(payload_len_small == 126) {
payload_len &= 0xffff;
len_size = 2;
for(i = 0; i < len_size; i++) {
memcpy(data+2+i, (void *)&payload_len+(len_size-i-1), 1);
}
}
if(payload_len_small == 127) {
payload_len &= 0xffffffffffffffffLL;
len_size = 8;
for(i = 0; i < len_size; i++) {
memcpy(data+2+i, (void *)&payload_len+(len_size-i-1), 1);
}
}
for(i=0;i<4;i++)
*(data+(payload_offset-4)+i) = mask[i];
memcpy(data+payload_offset, strdata, strlen(strdata));
for(i=0;i<strlen(strdata);i++)
*(data+payload_offset+i) ^= mask[i % 4] & 0xff;
sent = 0;
while(sent < frame_size) {
sent += send(sockfd, data+sent, frame_size - sent, 0);
}
free(data);
return sent;
}

@ -0,0 +1,29 @@
#include <stdint.h>
#ifndef WSCLIENT_H_
#define WSCLIENT_H_
#define CLIENT_IS_SSL (1 << 0)
#define REQUEST_HAS_CONNECTION (1 << 0)
#define REQUEST_HAS_UPGRADE (1 << 1)
#define REQUEST_VALID_STATUS (1 << 2)
#define REQUEST_VALID_ACCEPT (1 << 3)
typedef struct _wsclient {
int sockfd;
int flags;
int (*onopen)(void);
int (*onclose)(void);
int (*onerror)(void);
int (*onmessage)(char *message, int64_t length);
void (*run)(void);
} wsclient;
//Function defs
wsclient *libwsclient_new(const char *URI);
int libwsclient_open_connection(const char *host, const char *port);
int stricmp(const char *s1, const char *s2);
#endif /* WSCLIENT_H_ */
Loading…
Cancel
Save