mirror of
https://github.com/payden/libwsclient
synced 2024-10-27 17:54:01 +00:00
Initial commit of libwsclient
Only got the handshake and basic text frame sending done.
This commit is contained in:
commit
d01f0a9e13
42
.gitignore
vendored
Normal file
42
.gitignore
vendored
Normal file
@ -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
|
5
Makefile.am
Normal file
5
Makefile.am
Normal file
@ -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
|
5
autogen.sh
Executable file
5
autogen.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
libtoolize
|
||||||
|
aclocal \
|
||||||
|
&& automake --add-missing \
|
||||||
|
&& autoconf
|
215
base64.c
Normal file
215
base64.c
Normal file
@ -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
Symbolic link
1
config.guess
vendored
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
/usr/share/automake-1.11/config.guess
|
97
config.h.in
Normal file
97
config.h.in
Normal file
@ -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
Symbolic link
1
config.sub
vendored
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
/usr/share/automake-1.11/config.sub
|
28
configure.ac
Normal file
28
configure.ac
Normal file
@ -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
Normal file
371
sha1.c
Normal file
@ -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);
|
||||||
|
}
|
54
sha1.h
Normal file
54
sha1.h
Normal file
@ -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
|
29
test.c
Normal file
29
test.c
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
300
wsclient.c
Normal file
300
wsclient.c
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
29
wsclient.h
Normal file
29
wsclient.h
Normal file
@ -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…
Reference in New Issue
Block a user