├── .gitignore ├── html ├── favicon.ico ├── twitter-logo-1.png ├── BASEHTML.html ├── VERIFY_OK.html └── VERIFY_FAILED.html ├── release └── twittrouter_0.1.5-1_ar71xx.ipk ├── src ├── xmalloc.h ├── jconf.h ├── Makefile ├── AddressUtility.c ├── twittrouter.h ├── utils.h ├── xmalloc.c ├── arplinklist.c ├── config.h ├── utils.c ├── jconf.c ├── twittrouter.c ├── json.h ├── twitter.c ├── sha1.c ├── TCPServerUtility.c ├── hash.c ├── json.c ├── oauth.h └── oauth.c ├── config ├── twittrouter.json ├── network-pppoe └── twittrouter.init ├── README.md └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.o 3 | twittrouter 4 | -------------------------------------------------------------------------------- /html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scola/twittrouter/HEAD/html/favicon.ico -------------------------------------------------------------------------------- /html/twitter-logo-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scola/twittrouter/HEAD/html/twitter-logo-1.png -------------------------------------------------------------------------------- /release/twittrouter_0.1.5-1_ar71xx.ipk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scola/twittrouter/HEAD/release/twittrouter_0.1.5-1_ar71xx.ipk -------------------------------------------------------------------------------- /src/xmalloc.h: -------------------------------------------------------------------------------- 1 | #ifndef _OAUTH_XMALLOC_H 2 | #define _OAUTH_XMALLOC_H 1 3 | 4 | /* Prototypes for functions defined in xmalloc.c */ 5 | void *xmalloc (size_t size); 6 | void *xcalloc (size_t nmemb, size_t size); 7 | void *xrealloc (void *ptr, size_t size); 8 | char *xstrdup (const char *s); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /config/twittrouter.json: -------------------------------------------------------------------------------- 1 | { 2 | "TwitterID":"twitrouter", 3 | "CONSUMER_KEY":"yo9tIaQs7prILLMSq3DQiQ", 4 | "CONSUMER_SECRET":"EwOoyEkpb9STlZE6F0HtqofHhcPPhbpUpQel5lWoM", 5 | "OAUTH_TOKEN":"2207128206-MKRZH8xRwZaCvFrErjZPvR4DdrKUpEFax2kdKkQ", 6 | "OAUTH_TOKEN_SECRET":"k8rcOddAtKa86deKBLLl5yEvweBwvDJJ9QI3ItboJRz4J", 7 | "whitelist":"00:00:00:00:00:00|d8:57:ef:33:86:93" 8 | } 9 | -------------------------------------------------------------------------------- /src/jconf.h: -------------------------------------------------------------------------------- 1 | #ifndef _JCONF_H 2 | #define _JCONF_H 3 | 4 | #define MAX_CONF_SIZE 16 * 1024 5 | 6 | typedef struct 7 | { 8 | char *whitelist; 9 | char *TwitterID; 10 | char *CONSUMER_KEY; 11 | char *CONSUMER_SECRET; 12 | char *OAUTH_TOKEN; 13 | char *OAUTH_TOKEN_SECRET; 14 | } jconf_t; 15 | 16 | jconf_t *read_jconf(const char* file); 17 | void dump_jconf(char *conf_path); 18 | 19 | #endif // _JCONF_H 20 | -------------------------------------------------------------------------------- /config/network-pppoe: -------------------------------------------------------------------------------- 1 | 2 | config interface 'loopback' 3 | option ifname 'lo' 4 | option proto 'static' 5 | option ipaddr '127.0.0.1' 6 | option netmask '255.0.0.0' 7 | 8 | config globals 'globals' 9 | option ula_prefix 'fdb5:83ad:5d0c::/48' 10 | 11 | config interface 'lan' 12 | option type 'bridge' 13 | option proto 'static' 14 | option ipaddr '192.168.1.1' 15 | option netmask '255.255.255.0' 16 | 17 | config interface 'wan' 18 | option proto 'pppoe' 19 | option ifname 'eth0' 20 | option username '080020335913' 21 | option password '768729' 22 | 23 | -------------------------------------------------------------------------------- /config/twittrouter.init: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | # Copyright (C) 2006-2011 OpenWrt.org 3 | 4 | START=98 5 | STOP=98 6 | EXTRA_COMMANDS="restart" 7 | PIDFILE='/tmp/twittrouter.pid' 8 | PORT=9999 9 | 10 | start() { 11 | echo "starting twittrouter..." 12 | if [ -f $PIDFILE ] 13 | then 14 | echo "already started: $PIDFILE exists" 15 | exit 1 16 | fi 17 | twittrouter -p $PORT >/tmp/log/twittrouter.log 2>&1 & 18 | echo $! > $PIDFILE 19 | } 20 | 21 | stop() { 22 | echo "twittrouter stopped." 23 | for ip in `iptables -t nat -L PREROUTING -n | grep "redir ports $PORT"|awk '{print $4}'` 24 | do 25 | iptables -t nat -D PREROUTING -p tcp -s $ip --dport 80 -j REDIRECT --to-ports $PORT 26 | done 27 | kill `cat $PIDFILE` 28 | rm $PIDFILE 29 | } 30 | 31 | restart() { 32 | stop 33 | start 34 | } 35 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # build twittrouter executable when user executes "make" 2 | CC=gcc 3 | LDFLAGS=-lm -lcurl -lpthread 4 | CFLAGS=-std=gnu99 -Wall 5 | twittrouter: twittrouter.o TCPServerUtility.o AddressUtility.o utils.o jconf.o json.o arplinklist.o twitter.o hash.o oauth.o xmalloc.o 6 | $(CC) $(LDFLAGS) twittrouter.o TCPServerUtility.o AddressUtility.o utils.o jconf.o json.o arplinklist.o twitter.o hash.o oauth.o xmalloc.o -o twittrouter 7 | twittrouter.o: twittrouter.c 8 | $(CC) $(CFLAGS) -c twittrouter.c 9 | TCPServerUtility.o: TCPServerUtility.c 10 | $(CC) $(CFLAGS) -c TCPServerUtility.c 11 | AddressUtility.o: AddressUtility.c 12 | $(CC) $(CFLAGS) -c AddressUtility.c 13 | utils.o: utils.c 14 | $(CC) $(CFLAGS) -c utils.c 15 | jconf.o: jconf.c 16 | $(CC) $(CFLAGS) -c jconf.c 17 | json.o: json.c 18 | $(CC) $(CFLAGS) -c json.c 19 | arplinklist.o: arplinklist.c 20 | $(CC) $(CFLAGS) -c arplinklist.c 21 | twitter.o: twitter.c 22 | $(CC) $(CFLAGS) -c twitter.c 23 | hash.o: hash.c 24 | $(CC) $(CFLAGS) -c hash.c 25 | oauth.o: oauth.c 26 | $(CC) $(CFLAGS) -c oauth.c 27 | xmalloc.o: xmalloc.c 28 | $(CC) $(CFLAGS) -c xmalloc.c 29 | 30 | # remove object files and executable when user executes "make clean" 31 | clean: 32 | rm *.o twittrouter 33 | -------------------------------------------------------------------------------- /src/AddressUtility.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | char* PrintSocketAddress(const struct sockaddr *address, FILE *stream, int flag) { 8 | // Test for address and stream 9 | if (address == NULL || stream == NULL) 10 | return NULL; 11 | 12 | void *numericAddress; // Pointer to binary address 13 | // Buffer to contain result (IPv6 sufficient to hold IPv4) 14 | char addrBuffer[INET6_ADDRSTRLEN]; 15 | in_port_t port; // Port to print 16 | // Set pointer to address based on address family 17 | switch (address->sa_family) { 18 | case AF_INET: 19 | numericAddress = &((struct sockaddr_in *) address)->sin_addr; 20 | port = ntohs(((struct sockaddr_in *) address)->sin_port); 21 | break; 22 | case AF_INET6: 23 | numericAddress = &((struct sockaddr_in6 *) address)->sin6_addr; 24 | port = ntohs(((struct sockaddr_in6 *) address)->sin6_port); 25 | break; 26 | default: 27 | fputs("[unknown type]", stream); // Unhandled type 28 | return NULL; 29 | } 30 | // Convert binary to printable address 31 | if (inet_ntop(address->sa_family, numericAddress, addrBuffer, 32 | sizeof(addrBuffer)) == NULL) { 33 | fputs("[invalid address]", stream); // Unable to convert 34 | return NULL; 35 | } 36 | else { 37 | fprintf(stream, "%s", addrBuffer); 38 | if (port != 0) // Zero not valid in any socket addr 39 | fprintf(stream, "-%u", port); 40 | if(flag) { 41 | char *addr = (char *)malloc(INET6_ADDRSTRLEN); 42 | memset(addr,0,INET6_ADDRSTRLEN); 43 | memcpy(addr,addrBuffer,INET6_ADDRSTRLEN); 44 | return addr; 45 | } else { 46 | return NULL; 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/twittrouter.h: -------------------------------------------------------------------------------- 1 | #ifndef PRACTICAL_H_ 2 | #define PRACTICAL_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "jconf.h" 9 | 10 | #define VERSION "0.1.5" 11 | 12 | #define TWITTER_USERNAME_MAX_LEN 20 13 | 14 | #define DEFAULT_SERVER_DIR "/www/twittrouter" 15 | #define DEFAULT_SERVER_PORT "9999" 16 | #define DEFAULT_CONFIG_PATH "/etc/config/twittrouter.json" 17 | 18 | typedef struct _node_ 19 | { 20 | char ipaddr[16]; 21 | int ipType; 22 | struct _node_ *next; 23 | 24 | } linknode,*linklist; 25 | 26 | void Update (linklist p,int ipType); 27 | linklist Query (linklist p,char* ipaddr ); 28 | linklist CreatEmptyLink ( ); 29 | 30 | // Print socket address 31 | char* PrintSocketAddress(const struct sockaddr *address, FILE *stream, int flag); 32 | // Create, bind, and listen a new TCP server socket 33 | int SetupTCPServerSocket(const char *service); 34 | // Accept a new TCP connection on a server socket 35 | int AcceptTCPConnection(int servSock); 36 | // Handle new TCP client 37 | void HandleTCPClient(int clntSocket); 38 | // Get twitter user friendship 39 | bool get_friendship(char *username); 40 | // request oauth url 41 | void request_token_example_get(void); 42 | // get access token 43 | bool access_token_example_get(char *pin); 44 | //execute the shell command 45 | char* exec_cmd_shell(char *cmd); 46 | //scan arp to get connected client and block the one not in the whitelist 47 | void scan_arp_and_block(char *arpOutput); 48 | 49 | extern jconf_t *conf; 50 | extern char *root; 51 | extern char *servPort; 52 | extern linklist arpList; 53 | 54 | enum sizeConstants { 55 | MAXSTRINGLENGTH = 128, 56 | BUFSIZE = 4096, 57 | }; 58 | 59 | enum ipFlag { 60 | OAUTHED_FLAG = 0, 61 | BLOCKED_FLAG = 1, 62 | }; 63 | 64 | #endif // PRACTICAL_H_ 65 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTILS_H 2 | #define _UTILS_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef ANDROID 8 | 9 | #include 10 | 11 | #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "shadowsocks", __VA_ARGS__)) 12 | #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "shadowsocks", __VA_ARGS__)) 13 | 14 | #else 15 | 16 | #define STR(x) #x 17 | #define TOSTR(x) STR(x) 18 | 19 | #ifdef _WIN32 20 | 21 | #define TIME_FORMAT "%Y-%m-%d %H:%M:%S" 22 | 23 | #define LOGD(format, ...) do {\ 24 | time_t now = time(NULL);\ 25 | char timestr[20];\ 26 | strftime(timestr, 20, TIME_FORMAT, localtime(&now));\ 27 | fprintf(stderr, " %s INFO: " format "\n", timestr, ##__VA_ARGS__);}\ 28 | while(0) 29 | 30 | #define LOGE(format, ...) do {\ 31 | time_t now = time(NULL);\ 32 | char timestr[20];\ 33 | strftime(timestr, 20, TIME_FORMAT, localtime(&now));\ 34 | fprintf(stderr, " %s ERROR: " format "\n", timestr, ##__VA_ARGS__);}\ 35 | while(0) 36 | 37 | #else 38 | 39 | #define TIME_FORMAT "%F %T" 40 | 41 | #define LOGD(format, ...) do {\ 42 | time_t now = time(NULL);\ 43 | char timestr[20];\ 44 | strftime(timestr, 20, TIME_FORMAT, localtime(&now));\ 45 | fprintf(stderr, "\e[01;32m %s INFO: \e[0m" format "\n", timestr, ##__VA_ARGS__);}\ 46 | while(0) 47 | 48 | #define LOGE(format, ...) do {\ 49 | time_t now = time(NULL);\ 50 | char timestr[20];\ 51 | strftime(timestr, 20, TIME_FORMAT, localtime(&now));\ 52 | fprintf(stderr, "\e[01;35m %s ERROR: \e[0m" format "\n", timestr, ##__VA_ARGS__);}\ 53 | while(0) 54 | 55 | #endif 56 | /* _WIN32 */ 57 | 58 | #endif 59 | 60 | #ifdef __MINGW32__ 61 | 62 | #ifdef ERROR 63 | #undef ERROR 64 | #endif 65 | #define ERROR(s) ss_error(s) 66 | 67 | char *ss_itoa(int i); 68 | 69 | #else 70 | 71 | void ERROR(const char *s); 72 | char *itoa(int i); 73 | 74 | #endif 75 | 76 | void FATAL(const char *msg); 77 | void usage(void); 78 | void demonize(const char* path); 79 | char *ss_strndup(const char *s, size_t n); 80 | char *str_replace(char *orig, char *rep, char *with); 81 | 82 | #endif // _UTILS_H 83 | -------------------------------------------------------------------------------- /src/xmalloc.c: -------------------------------------------------------------------------------- 1 | /* xmalloc.c -- memory allocation including 'out of memory' checks 2 | * 3 | * Copyright 2010 Robin Gareus 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | * 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | static void *xmalloc_fatal(size_t size) { 31 | if (size==0) return NULL; 32 | fprintf(stderr, "Out of memory."); 33 | exit(1); 34 | } 35 | 36 | void *xmalloc (size_t size) { 37 | void *ptr = malloc (size); 38 | if (ptr == NULL) return xmalloc_fatal(size); 39 | return ptr; 40 | } 41 | 42 | void *xcalloc (size_t nmemb, size_t size) { 43 | void *ptr = calloc (nmemb, size); 44 | if (ptr == NULL) return xmalloc_fatal(nmemb*size); 45 | return ptr; 46 | } 47 | 48 | void *xrealloc (void *ptr, size_t size) { 49 | void *p = realloc (ptr, size); 50 | if (p == NULL) return xmalloc_fatal(size); 51 | return p; 52 | } 53 | 54 | char *xstrdup (const char *s) { 55 | void *ptr = xmalloc(strlen(s)+1); 56 | strcpy (ptr, s); 57 | return (char*) ptr; 58 | } 59 | 60 | // vi: sts=2 sw=2 ts=2 61 | -------------------------------------------------------------------------------- /src/arplinklist.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "twittrouter.h" 10 | #include "xmalloc.h" 11 | #include "utils.h" 12 | 13 | linklist CreatEmptyLink ( ) // creat an empty link;return the head dress of the link 14 | { 15 | linklist h = (linklist)malloc(sizeof(linknode)); 16 | h->next = NULL; 17 | return h; 18 | } 19 | 20 | static void InsertEmptyLink (linklist p,char* ipaddr,int ipType) // insert a node at the head of the link 21 | { 22 | linklist h = (linklist)malloc(sizeof(linknode)); 23 | memset((void *)h, 0, sizeof(linknode)); 24 | strcpy(h -> ipaddr, ipaddr); 25 | h -> ipType = ipType; 26 | h -> next = p-> next; 27 | p -> next = h; 28 | } 29 | 30 | linklist Query (linklist p,char* ipaddr ) { 31 | while (p->next) { 32 | if (strcmp(p->next->ipaddr,ipaddr) == 0) { 33 | return p->next; 34 | } 35 | p = p->next; 36 | } 37 | return NULL; 38 | } 39 | 40 | static void CheckList (linklist p) { 41 | while (p->next) { 42 | LOGD("the current list ipaddr == %s,ipType = %d \n",p->next->ipaddr,p->next->ipType); 43 | p = p->next; 44 | } 45 | } 46 | 47 | void Update (linklist p,int ipType) { 48 | p -> ipType = ipType; 49 | } 50 | 51 | static bool isValidIpAddress(char *ipAddress) 52 | { 53 | struct sockaddr_in sa; 54 | int result = inet_pton(AF_INET, ipAddress, &(sa.sin_addr)); 55 | return result != 0; 56 | } 57 | 58 | static bool isValidMAC(char *s) { 59 | int i; 60 | for(i = 0; i < 17; i++) { 61 | if(i % 3 != 2 && !isxdigit(s[i])) 62 | return false; 63 | if(i % 3 == 2 && s[i] != ':' && s[i] != '-') 64 | return false; 65 | } 66 | if(s[17] != '\0') 67 | return false; 68 | return true; 69 | } 70 | 71 | char* exec_cmd_shell(char *cmd) { 72 | FILE *in = popen (cmd, "r"); 73 | size_t len = 0; 74 | size_t alloc = 0; 75 | char *data = NULL; 76 | int rcv = 1; 77 | while (in && rcv > 0 && !feof(in)) { 78 | alloc +=1024; 79 | data = (char*)xrealloc(data, alloc * sizeof(char)); 80 | rcv = fread(data + (alloc-1024), sizeof(char), 1024, in); 81 | len += rcv; 82 | } 83 | pclose(in); 84 | data[len]=0; 85 | if (data) printf("%s\n",data); 86 | return data; 87 | 88 | } 89 | 90 | void scan_arp_and_block(char *arpOutput) { 91 | char *line = strtok(arpOutput,"\n"); 92 | char *ip; 93 | char iptable_block_cmd[MAXSTRINGLENGTH] = {'\0',}; 94 | //char iptable_unblock_cmd[MAXSTRINGLENGTH] = {'\0'}; 95 | 96 | while (line) { 97 | line = strtok(NULL," \t\n"); 98 | if (!line) break; 99 | //printf("split the line and get %s\n",line); 100 | if (isValidIpAddress(line)) { 101 | ip = line; 102 | continue; 103 | } 104 | if (isValidMAC(line)) { 105 | if(strstr(conf->whitelist,line) || Query(arpList,ip)) { 106 | continue; 107 | } else { 108 | InsertEmptyLink(arpList,ip,BLOCKED_FLAG); 109 | sprintf(iptable_block_cmd,"iptables -t nat -I PREROUTING -s %s -p tcp --dport 80 -j REDIRECT --to-ports %s", ip, servPort); 110 | char *block_cmd_output = exec_cmd_shell(iptable_block_cmd); 111 | if(!block_cmd_output) free(block_cmd_output); 112 | LOGD("blocked ip addr %s \n",ip); 113 | } 114 | } 115 | } 116 | 117 | CheckList(arpList); 118 | } 119 | -------------------------------------------------------------------------------- /html/BASEHTML.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 欢迎使用我家Wi-Fi 7 | 8 | 9 | 10 | 11 | 91 | 92 | 93 | 94 | 95 | 96 |
97 |
98 |

你是我的推友么

99 |

100 | 必须要成为Wi-Fi主人的Twitter好友
101 | 才能无限制使用他家的Wi-Fi
102 | 已经关注Wi-Fi主人@twitterid
103 | 请输入你的Twitter用户名 104 | 105 |

106 | 用户名:@ 107 |
108 | 109 |
110 | 如果不是Wi-Fi主人的Twitter好友
立即关注,关注后
111 | 你就可以无限制享用此Wi-Fi了 112 | 113 |

114 | 115 | 116 | 117 | 118 | 119 |
120 |
121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Twittrouter 2 | =========== 3 | Twittrouter is used to verify your wifi client by twitter friends.It runs on [openwrt](https://openwrt.org/) router.Maybe it also support on dd-wrt or tomato router,I have not tested it. 4 | 5 | [中文说明](http://scola.github.io/update-twittrouter-about-auth-and-arp-method/) 6 | How to build 7 | ------------ 8 | Build the source code,this project requires [liboauth](http://liboauth.sourceforge.net/) library,but I have include in the source code for static build 9 | ```bash 10 | # At OpenWRT build root 11 | pushd package 12 | git clone https://github.com/scola/twittrouter.git 13 | popd 14 | 15 | # Enable twittrouter in network category 16 | make menuconfig 17 | 18 | # Optional 19 | make -j 20 | 21 | # Build the package 22 | make V=s package/twittrouter/compile 23 | ``` 24 | 25 | Usage 26 | ----- 27 | If you trust me,you can skip the build step and [download the ipk package here](https://github.com/scola/twittrouter/tree/master/release) and install.You must config your source address correctly in /etc/opkg.conf.iptables are required 28 | 29 | opkg install iptables 30 | 31 | and the install twittrouter ipk packages,it will automatically download and install the required library(libcurl,libpthread) 32 | 33 | opkg update 34 | opkg install twittrouter 35 | 36 | Get the usage of twittrouter 37 | 38 | twittrouter -h 39 | 40 | Your own devices need not to be verified,so just add it into whitelist.00:00:00:00:00:00 is invalid mac address,so keep it in whitelist.You could append your device mac address that's split by '|'. 41 | 42 | Edit /etc/conf/twittrouter.json 43 | 44 | "whitelist":"00:00:00:00:00:00|d8:57:ef:33:86:93" 45 | 46 | Run this program,and I recommend you add this program when system startup. 47 | 48 | twittrouter -a #authorize your own twitter username 49 | twittrouter #run this application 50 | /etc/init.d/twittrouter enable #execute twittrouter when system startup 51 | 52 | Chinese user only 53 | ----------------- 54 | Because of the evil GFW,chinese user must make your route cross the GFW.You can take a look of [my blog](http://scola.github.io/deploy-proxy-on-openwrt--client-need-not-to-set/).Of course, you can use other network tools,such as VPN.**It makes no sense to run this program on your router unless your wifi client can cross the GFW**.Because your wifi client need to connect to [twitter.com](https://twitter.com) without any setting.This program call twitter api and api.twitter.com is blocked too,so you must config your router to make your router cross the GFW internally.Please refer to [this topic](http://scola.github.io/add-twitter-follower-verification-over-wifi/) 55 | 56 | so I strongly suggest you test whether you configure you network correctly to go throuth the GFW. 57 | 58 | twittrouter -u [username] #test the oauth to check your network config firstly,username is one of you twitter friends. 59 | 60 | Known issues 61 | ------------- 62 | * until now the web page do not support english 63 | 64 | * the verify web page looks ugly on computer 65 | 66 | Thanks 67 | ------ 68 | Thanks to the developer of [goagent](https://code.google.com/p/goagent/),[shadowsocks](http://www.shadowsocks.org/),[dnsproxy](https://github.com/phuslu/dnsproxy), [bestroutetb](https://github.com/ashi009/bestroutetb),[liboauth](https://github.com/x42/liboauth) and other bloggers 69 | 70 | License 71 | ------- 72 | Copyright (C) 2014 Scola 73 | 74 | This program is free software: you can redistribute it and/or modify 75 | it under the terms of the GNU General Public License as published by 76 | the Free Software Foundation, either version 3 of the License, or 77 | (at your option) any later version. 78 | 79 | This program is distributed in the hope that it will be useful, 80 | but WITHOUT ANY WARRANTY; without even the implied warranty of 81 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 82 | GNU General Public License for more details. 83 | 84 | You should have received a copy of the GNU General Public License 85 | along with this program. If not, see 86 | 87 | Screenshot 88 | ---------- 89 | ![verification-page.png](https://raw.github.com/scola/twittrouter-python/master/verification-page.png) 90 | -------------------------------------------------------------------------------- /html/VERIFY_OK.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 欢迎使用我家Wi-Fi 7 | 8 | 9 | 10 | 11 | 91 | 92 | 93 | 94 | 95 | 96 |
97 |
98 |

你是我的推友么

99 |

100 | 必须要成为Wi-Fi主人的Twitter好友
101 | 才能无限制使用他家的Wi-Fi
102 | 已经关注Wi-Fi主人@twitterid
103 | 请输入你的Twitter用户名 104 | 105 |

106 | 用户名:@ 107 |
108 | 109 |
110 | 如果不是Wi-Fi主人的Twitter好友
立即关注,关注后
111 | 你就可以无限制享用此Wi-Fi了 112 | 113 |

114 | 115 |

116 | 117 | 恭喜你!验证成功,现在你可以无限制使用此Wi-Fi了 118 | 119 |

120 | 121 | 122 | 123 | 124 |
125 |
126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /html/VERIFY_FAILED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 欢迎使用我家Wi-Fi 7 | 8 | 9 | 10 | 11 | 91 | 92 | 93 | 94 | 95 | 96 |
97 |
98 |

你是我的推友么

99 |

100 | 必须要成为Wi-Fi主人的Twitter好友
101 | 才能无限制使用他家的Wi-Fi
102 | 已经关注Wi-Fi主人@twitterid
103 | 请输入你的Twitter用户名 104 | 105 |

106 | 用户名:@ 107 |
108 | 109 |
110 | 如果不是Wi-Fi主人的Twitter好友
立即关注,关注后
111 | 你就可以无限制享用此Wi-Fi了 112 | 113 |

114 | 115 |

116 | 117 | 118 | 很遗憾,验证失败,请重新输入你的Twitter用户名 119 | 120 |

121 | 122 | 123 | 124 | 125 |
126 |
127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ############################################## 2 | # OpenWrt Makefile for twittrouter program 3 | # 4 | # 5 | # Most of the variables used here are defined in 6 | # the include directives below. We just need to 7 | # specify a basic description of the package, 8 | # where to build our program, where to find 9 | # the source files, and where to install the 10 | # compiled program on the router. 11 | # 12 | # Be very careful of spacing in this file. 13 | # Indents should be tabs, not spaces, and 14 | # there should be no trailing whitespace in 15 | # lines that are not commented. 16 | # 17 | ############################################## 18 | 19 | include $(TOPDIR)/rules.mk 20 | 21 | # Name and release number of this package 22 | PKG_NAME:=twittrouter 23 | PKG_VERSION:=0.1.5 24 | PKG_RELEASE:=1 25 | #PKG_RELEASE:=$(PKG_SOURCE_VERSION) 26 | 27 | #PKG_SOURCE_PROTO:=git 28 | #PKG_SOURCE_URL:=https://github.com/scola/twittrouter.git 29 | #PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) 30 | #PKG_SOURCE_VERSION:=e24bbb456e88f652afae0b05cb8299e59ff41ee2 31 | #PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz 32 | #PKG_MAINTAINER:=Scola 33 | 34 | #PKG_INSTALL:=1 35 | #PKG_FIXUP:=autoreconf 36 | 37 | #PKG_BUILD_PARALLEL:=1 38 | 39 | # This specifies the directory where we're going to build the program. 40 | # The root build directory, $(BUILD_DIR), is by default the build_mipsel 41 | # directory in your OpenWrt SDK directory 42 | PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) 43 | 44 | include $(INCLUDE_DIR)/package.mk 45 | 46 | # Specify package information for this program. 47 | # The variables defined here should be self explanatory. 48 | define Package/twittrouter 49 | SECTION:=net 50 | CATEGORY:=Network 51 | TITLE:=verify twitter friends on router 52 | URL:=https://github.com/scola/twittrouter 53 | DEPENDS:=+libcurl +libpthread 54 | endef 55 | 56 | define Package/twittrouter/description 57 | twittrouter replace the wifi password verification with twitter friends 58 | endef 59 | 60 | 61 | define Package/twittrouter/conffiles 62 | /etc/config/twittrouter.json 63 | endef 64 | # Specify what needs to be done to prepare for building the package. 65 | # In our case, we need to copy the source files to the build directory. 66 | # This is NOT the default. The default uses the PKG_SOURCE_URL and the 67 | # PKG_SOURCE which is not defined here to download the source from the web. 68 | # In order to just build a simple program that we have just written, it is 69 | # much easier to do it this way. 70 | define Build/Prepare 71 | mkdir -p $(PKG_BUILD_DIR) 72 | mkdir -p $(PKG_INSTALL_DIR)/www/twittrouter 73 | $(CP) ./src/* $(PKG_BUILD_DIR)/ 74 | endef 75 | 76 | # We do not need to define Build/Configure or Build/Compile directives 77 | # The defaults are appropriate for compiling a simple program such as this one 78 | 79 | # Specify where and how to install the program. Since we only have one file, 80 | # the twittrouter executable, install it by copying it to the /bin directory on 81 | # the router. The $(1) variable represents the root directory on the router running 82 | # OpenWrt. The $(INSTALL_DIR) variable contains a command to prepare the install 83 | # directory if it does not already exist. Likewise $(INSTALL_BIN) contains the 84 | # command to copy the binary file from its current location (in our case the build 85 | # directory) to the install directory. 86 | define Package/twittrouter/install 87 | $(INSTALL_DIR) $(1)/usr/bin $(1)/www/twittrouter $(1)/etc/init.d $(1)/etc/config 88 | $(INSTALL_BIN) $(PKG_BUILD_DIR)/twittrouter $(1)/usr/bin/ 89 | $(INSTALL_BIN) ./html/* $(1)/www/twittrouter 90 | $(INSTALL_BIN) ./config/twittrouter.init $(1)/etc/init.d/twittrouter 91 | $(INSTALL_CONF) ./config/twittrouter.json $(1)/etc/config/twittrouter.json 92 | $(INSTALL_CONF) ./config/pdnsd-ss-iptables.sh $(1)/etc/config/pdnsd-ss-iptables.sh 93 | $(INSTALL_CONF) ./config/network-pppoe $(1)/etc/config/network-pppoe 94 | endef 95 | 96 | # This line executes the necessary commands to compile our program. 97 | # The above define directives specify all the information needed, but this 98 | # line calls BuildPackage which in turn actually uses this information to 99 | # build a package. 100 | $(eval $(call BuildPackage,twittrouter)) 101 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | /* src/config.h. Generated from config.h.in by configure. */ 2 | /* src/config.h.in. Generated from configure.ac by autoheader. */ 3 | 4 | /* Define to 1 if the `closedir' function returns void instead of `int'. */ 5 | /* #undef CLOSEDIR_VOID */ 6 | 7 | /* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP 8 | systems. This function is required for `alloca.c' support on those systems. 9 | */ 10 | /* #undef CRAY_STACKSEG_END */ 11 | 12 | /* Define to 1 if using `alloca.c'. */ 13 | /* #undef C_ALLOCA */ 14 | 15 | /* Define to 1 if you have `alloca', as a function or macro. */ 16 | #define HAVE_ALLOCA 1 17 | 18 | /* Define to 1 if you have and it should be used (not on Ultrix). 19 | */ 20 | #define HAVE_ALLOCA_H 1 21 | 22 | /* Define as 1 if you have libcurl */ 23 | #define HAVE_CURL 1 24 | 25 | /* Define to 1 if you have the header file. */ 26 | /* #undef HAVE_CURL_CURL_H */ 27 | 28 | /* Define to 1 if you have the header file, and it defines `DIR'. 29 | */ 30 | #define HAVE_DIRENT_H 1 31 | 32 | /* Define to 1 if you have the header file. */ 33 | #define HAVE_DLFCN_H 1 34 | 35 | /* Define to 1 if you have the header file. */ 36 | #define HAVE_INTTYPES_H 1 37 | 38 | /* Define to 1 if you have the header file. */ 39 | #define HAVE_MATH_H 1 40 | 41 | /* Define to 1 if you have the header file. */ 42 | #define HAVE_MEMORY_H 1 43 | 44 | /* Define to 1 if you have the header file, and it defines `DIR'. */ 45 | /* #undef HAVE_NDIR_H */ 46 | 47 | /* Define to 1 if you have the header file. */ 48 | /* #undef HAVE_OPENSSL_HMAC_H */ 49 | 50 | /* Define if you can invoke curl via a shell command. This is only used if 51 | HAVE_CURL is not defined. */ 52 | #define HAVE_SHELL_CURL 1 53 | 54 | /* Define to 1 if you have the header file. */ 55 | #define HAVE_STDARG_H 1 56 | 57 | /* Define to 1 if you have the header file. */ 58 | #define HAVE_STDINT_H 1 59 | 60 | /* Define to 1 if you have the header file. */ 61 | #define HAVE_STDIO_H 1 62 | 63 | /* Define to 1 if you have the header file. */ 64 | #define HAVE_STDLIB_H 1 65 | 66 | /* Define to 1 if you have the header file. */ 67 | #define HAVE_STRINGS_H 1 68 | 69 | /* Define to 1 if you have the header file. */ 70 | #define HAVE_STRING_H 1 71 | 72 | /* Define to 1 if `st_blocks' is a member of `struct stat'. */ 73 | #define HAVE_STRUCT_STAT_ST_BLOCKS 1 74 | 75 | /* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use 76 | `HAVE_STRUCT_STAT_ST_BLOCKS' instead. */ 77 | #define HAVE_ST_BLOCKS 1 78 | 79 | /* Define to 1 if you have the header file, and it defines `DIR'. 80 | */ 81 | /* #undef HAVE_SYS_DIR_H */ 82 | 83 | /* Define to 1 if you have the header file, and it defines `DIR'. 84 | */ 85 | /* #undef HAVE_SYS_NDIR_H */ 86 | 87 | /* Define to 1 if you have the header file. */ 88 | #define HAVE_SYS_STAT_H 1 89 | 90 | /* Define to 1 if you have the header file. */ 91 | #define HAVE_SYS_TYPES_H 1 92 | 93 | /* Define to 1 if you have the header file. */ 94 | #define HAVE_TIME_H 1 95 | 96 | /* Define to 1 if you have the header file. */ 97 | #define HAVE_UNISTD_H 1 98 | 99 | /* Define to the sub-directory in which libtool stores uninstalled libraries. 100 | */ 101 | #define LT_OBJDIR ".libs/" 102 | 103 | /* Define to 1 if `major', `minor', and `makedev' are declared in . 104 | */ 105 | /* #undef MAJOR_IN_MKDEV */ 106 | 107 | /* Define to 1 if `major', `minor', and `makedev' are declared in 108 | . */ 109 | /* #undef MAJOR_IN_SYSMACROS */ 110 | 111 | /* Define to 1 if your C compiler doesn't accept -c and -o together. */ 112 | /* #undef NO_MINUS_C_MINUS_O */ 113 | 114 | /* Define the number of seconds for the HTTP request to timeout; if not 115 | defined no timeout (or libcurl default) is used. */ 116 | /* #undef OAUTH_CURL_TIMEOUT */ 117 | 118 | /* Name of package */ 119 | #define PACKAGE "liboauth" 120 | 121 | /* Define to the address where bug reports for this package should be sent. */ 122 | #define PACKAGE_BUGREPORT "robin AT gareus DOT org" 123 | 124 | /* Define to the full name of this package. */ 125 | #define PACKAGE_NAME "liboauth" 126 | 127 | /* Define to the full name and version of this package. */ 128 | #define PACKAGE_STRING "liboauth -" 129 | 130 | /* Define to the one symbol short name of this package. */ 131 | #define PACKAGE_TARNAME "liboauth" 132 | 133 | /* Define to the home page for this package. */ 134 | #define PACKAGE_URL "http://liboauth.sourceforge.net/" 135 | 136 | /* Define to the version of this package. */ 137 | #define PACKAGE_VERSION "-" 138 | 139 | /* If using the C implementation of alloca, define if you know the 140 | direction of stack growth for your system; otherwise it will be 141 | automatically deduced at runtime. 142 | STACK_DIRECTION > 0 => grows toward higher addresses 143 | STACK_DIRECTION < 0 => grows toward lower addresses 144 | STACK_DIRECTION = 0 => direction of growth unknown */ 145 | /* #undef STACK_DIRECTION */ 146 | 147 | /* Define to 1 if you have the ANSI C header files. */ 148 | #define STDC_HEADERS 1 149 | 150 | /* Define to 1 if your declares `struct tm'. */ 151 | /* #undef TM_IN_SYS_TIME */ 152 | 153 | /* Define to use neither NSS nor OpenSSL */ 154 | #define USE_BUILTIN_HASH 1 155 | 156 | /* Define to use NSS instead of OpenSSL */ 157 | /* #undef USE_NSS */ 158 | 159 | /* Version number of package */ 160 | #define VERSION "1.0.1" 161 | 162 | /* Define to `unsigned int' if does not define. */ 163 | /* #undef size_t */ 164 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "utils.h" 10 | #include "twittrouter.h" 11 | 12 | #ifdef HAVE_CONFIG_H 13 | #include "config.h" 14 | #endif 15 | 16 | #define INT_DIGITS 19 /* enough for 64 bit integer */ 17 | 18 | 19 | #ifndef __MINGW32__ 20 | void ERROR(const char *s) 21 | { 22 | char *msg = strerror(errno); 23 | LOGE("%s: %s", s, msg); 24 | 25 | } 26 | #endif 27 | 28 | #ifdef __MINGW32__ 29 | char *ss_itoa(int i) 30 | #else 31 | char *itoa(int i) 32 | #endif 33 | { 34 | /* Room for INT_DIGITS digits, - and '\0' */ 35 | static char buf[INT_DIGITS + 2]; 36 | char *p = buf + INT_DIGITS + 1; /* points to terminating '\0' */ 37 | if (i >= 0) 38 | { 39 | do 40 | { 41 | *--p = '0' + (i % 10); 42 | i /= 10; 43 | } 44 | while (i != 0); 45 | return p; 46 | } 47 | else /* i < 0 */ 48 | { 49 | do 50 | { 51 | *--p = '0' - (i % 10); 52 | i /= 10; 53 | } 54 | while (i != 0); 55 | *--p = '-'; 56 | } 57 | return p; 58 | } 59 | 60 | char *ss_strndup(const char *s, size_t n) 61 | { 62 | size_t len = strlen(s); 63 | char *ret; 64 | 65 | if (len <= n) return strdup(s); 66 | 67 | ret = malloc(n + 1); 68 | strncpy(ret, s, n); 69 | ret[n] = '\0'; 70 | return ret; 71 | } 72 | 73 | void FATAL(const char *msg) 74 | { 75 | LOGE("%s", msg); 76 | exit(-1); 77 | } 78 | 79 | // You must free the result if result is non-NULL. 80 | char *str_replace(char *orig, char *rep, char *with) { 81 | char *result; // the return string 82 | char *ins; // the next insert point 83 | char *tmp; // varies 84 | int len_rep; // length of rep 85 | int len_with; // length of with 86 | int len_front; // distance between rep and end of last rep 87 | int count; // number of replacements 88 | 89 | if (!orig) 90 | return NULL; 91 | if (!rep) 92 | rep = ""; 93 | len_rep = strlen(rep); 94 | if (!with) 95 | with = ""; 96 | len_with = strlen(with); 97 | 98 | ins = orig; 99 | for (count = 0; (tmp = strstr(ins, rep)) != NULL; ++count) { 100 | ins = tmp + len_rep; 101 | } 102 | 103 | // first time through the loop, all the variable are set correctly 104 | // from here on, 105 | // tmp points to the end of the result string 106 | // ins points to the next occurrence of rep in orig 107 | // orig points to the remainder of orig after "end of rep" 108 | tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1); 109 | 110 | if (!result) 111 | return NULL; 112 | 113 | while (count--) { 114 | ins = strstr(orig, rep); 115 | len_front = ins - orig; 116 | tmp = strncpy(tmp, orig, len_front) + len_front; 117 | tmp = strcpy(tmp, with) + len_with; 118 | orig += len_front + len_rep; // move to next "end of rep" 119 | } 120 | strcpy(tmp, orig); 121 | return result; 122 | } 123 | 124 | void usage() 125 | { 126 | printf("\n"); 127 | printf("twittrouter --version %s\n\n", VERSION); 128 | printf(" maintained by Scola \n\n"); 129 | printf(" usage:\n\n"); 130 | printf(" Firstly,test whether the default oauth works normally\n"); 131 | printf(" Try to run [twittrouter -u kfc]\n"); 132 | printf(" Second,authorize your own twitter account\n"); 133 | printf(" Try to run [twittrouter -a]\n"); 134 | printf(" If the two step above works normally,then run [/etc/init.d/twittrouter start]\n"); 135 | printf(" Optional,you can add your own device mac address into whitelist of /etc/config/twittrouter.json\n"); 136 | printf("\n"); 137 | printf(" [-p ] server port,the default value is 9999\n"); 138 | printf(" [-c ] twittrouter.json path,default /etc/config/twittrouter.json\n"); 139 | printf(" [-r ] html file path for the server,default /www/twittrouter\n"); 140 | printf(" [-h ] get the usage of the twittrouter\n"); 141 | printf(" [-u ] just use to test the oauth and network config\n"); 142 | printf(" [-a ] authorize and get twitter acess token\n"); 143 | printf("\n"); 144 | } 145 | 146 | void demonize(const char* path) 147 | { 148 | #ifndef __MINGW32__ 149 | /* Our process ID and Session ID */ 150 | pid_t pid, sid; 151 | 152 | /* Fork off the parent process */ 153 | pid = fork(); 154 | if (pid < 0) 155 | { 156 | exit(EXIT_FAILURE); 157 | } 158 | 159 | /* If we got a good PID, then 160 | we can exit the parent process. */ 161 | if (pid > 0) 162 | { 163 | FILE *file = fopen(path, "w"); 164 | if (file == NULL) FATAL("Invalid pid file\n"); 165 | 166 | fprintf(file, "%d", pid); 167 | fclose(file); 168 | exit(EXIT_SUCCESS); 169 | } 170 | 171 | /* Change the file mode mask */ 172 | umask(0); 173 | 174 | /* Open any logs here */ 175 | 176 | /* Create a new SID for the child process */ 177 | sid = setsid(); 178 | if (sid < 0) 179 | { 180 | /* Log the failure */ 181 | exit(EXIT_FAILURE); 182 | } 183 | 184 | /* Change the current working directory */ 185 | if ((chdir("/")) < 0) 186 | { 187 | /* Log the failure */ 188 | exit(EXIT_FAILURE); 189 | } 190 | 191 | /* Close out the standard file descriptors */ 192 | close(STDIN_FILENO); 193 | close(STDOUT_FILENO); 194 | close(STDERR_FILENO); 195 | #endif 196 | } 197 | 198 | -------------------------------------------------------------------------------- /src/jconf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "utils.h" 8 | #include "jconf.h" 9 | #include "json.h" 10 | #include "string.h" 11 | 12 | static char *to_string(const json_value *value) 13 | { 14 | if (value->type == json_string) 15 | { 16 | return ss_strndup(value->u.string.ptr, value->u.string.length); 17 | } 18 | else if (value->type == json_integer) 19 | { 20 | #ifdef __MINGW32__ 21 | return strdup(ss_itoa(value->u.integer)); 22 | #else 23 | return strdup(itoa(value->u.integer)); 24 | #endif 25 | } 26 | else if (value->type == json_null) 27 | { 28 | return "null"; 29 | } 30 | else 31 | { 32 | LOGE("%d", value->type); 33 | FATAL("Invalid config format."); 34 | } 35 | return 0; 36 | } 37 | 38 | static char* packstring(char *origin, char *delm) { 39 | int packed_len = strlen(origin) + 2 * strlen(delm) + 1; 40 | char *ret = (char *)malloc(packed_len); 41 | memset(ret,0,packed_len); 42 | strcat(ret,delm); 43 | strcat(ret,origin); 44 | strcat(ret,delm); 45 | return ret; 46 | } 47 | 48 | static char* concatstring(char *origin, char *delm, char *tail) { 49 | int concat_len = strlen(origin) + strlen(delm) + strlen(tail) + 1; 50 | char *ret = (char *)malloc(concat_len); 51 | memset(ret,0,concat_len); 52 | strcat(ret,origin); 53 | strcat(ret,delm); 54 | strcat(ret,tail); 55 | return ret; 56 | } 57 | 58 | static char* build_json_item(char *key, char *value) { 59 | char *pack_key = packstring(key, "\""); 60 | char *pack_value = packstring(value, "\""); 61 | char *ret = concatstring(pack_key, ":", pack_value); 62 | if (pack_key) free(pack_key); 63 | if (pack_value) free(pack_value); 64 | return ret; 65 | } 66 | 67 | static int dump_to_file(const char *buffer, size_t size, char *file) 68 | { 69 | FILE *dest = fopen(file, "w"); 70 | if(fwrite(buffer, size, 1, dest) != 1) 71 | return -1; 72 | return 0; 73 | } 74 | 75 | jconf_t *read_jconf(const char* file) 76 | { 77 | 78 | static jconf_t conf; 79 | 80 | char *buf; 81 | json_value *obj; 82 | 83 | FILE *f = fopen(file, "r"); 84 | if (f == NULL) FATAL("Invalid config path."); 85 | 86 | fseek(f, 0, SEEK_END); 87 | long pos = ftell(f); 88 | fseek(f, 0, SEEK_SET); 89 | 90 | if (pos >= MAX_CONF_SIZE) FATAL("Too large config file."); 91 | 92 | buf = malloc(pos + 1); 93 | if (buf == NULL) FATAL("No enough memory."); 94 | 95 | fread(buf, pos, 1, f); 96 | fclose(f); 97 | 98 | buf[pos] = '\0'; // end of string 99 | 100 | json_settings settings = { 0 }; 101 | char error_buf[512]; 102 | obj = json_parse_ex(&settings, buf, pos, error_buf); 103 | 104 | if (obj == NULL) 105 | { 106 | FATAL(error_buf); 107 | } 108 | 109 | if (obj->type == json_object) 110 | { 111 | int i; 112 | for (i = 0; i < obj->u.object.length; i++) 113 | { 114 | char *name = obj->u.object.values[i].name; 115 | json_value *value = obj->u.object.values[i].value; 116 | if (strcmp(name, "whitelist") == 0) 117 | { 118 | conf.whitelist = to_string(value); 119 | } 120 | else if (strcmp(name, "TwitterID") == 0) 121 | { 122 | conf.TwitterID = to_string(value); 123 | } 124 | else if (strcmp(name, "CONSUMER_KEY") == 0) 125 | { 126 | conf.CONSUMER_KEY = to_string(value); 127 | } 128 | else if (strcmp(name, "CONSUMER_SECRET") == 0) 129 | { 130 | conf.CONSUMER_SECRET = to_string(value); 131 | } 132 | else if (strcmp(name, "OAUTH_TOKEN") == 0) 133 | { 134 | conf.OAUTH_TOKEN = to_string(value); 135 | } 136 | else if (strcmp(name, "OAUTH_TOKEN_SECRET") == 0) 137 | { 138 | conf.OAUTH_TOKEN_SECRET = to_string(value); 139 | } 140 | } 141 | } 142 | else 143 | { 144 | FATAL("Invalid config file"); 145 | } 146 | 147 | free(buf); 148 | json_value_free(obj); 149 | return &conf; 150 | 151 | } 152 | 153 | void dump_jconf(char *conf_path){ 154 | char* jsonlist[6]; 155 | extern jconf_t *conf; 156 | //jsonlist[0] = concatstring(packstring("TwitterID", "\""), ":" ,packstring(conf->whitelist, "\"")); 157 | jsonlist[0] = build_json_item("TwitterID",conf->TwitterID); 158 | jsonlist[1] = build_json_item("CONSUMER_KEY",conf->CONSUMER_KEY); 159 | jsonlist[2] = build_json_item("CONSUMER_SECRET",conf->CONSUMER_SECRET); 160 | jsonlist[3] = build_json_item("OAUTH_TOKEN",conf->OAUTH_TOKEN); 161 | jsonlist[4] = build_json_item("OAUTH_TOKEN_SECRET",conf->OAUTH_TOKEN_SECRET); 162 | jsonlist[5] = build_json_item("whitelist",conf->whitelist); 163 | 164 | int j; 165 | int stringlen = 0; 166 | for(j = 0; j < 6; j++) { 167 | stringlen += strlen(jsonlist[j]); 168 | } 169 | int total_len = stringlen + 5 * 4 + 1; 170 | char *all_item = (char *)malloc(total_len); 171 | memset(all_item,0, total_len); 172 | 173 | for(j = 0; j < 5; j++) { 174 | strcat(all_item,jsonlist[j]); 175 | strcat(all_item,",\r\n\t"); 176 | } 177 | strcat(all_item,jsonlist[5]); 178 | 179 | for(j = 0; j < 6; j++) { 180 | if (jsonlist[j]) free(jsonlist[j]); 181 | } 182 | 183 | char *final_string = (char *)malloc(total_len + 4 + 3 + 1); 184 | memset(final_string, 0, total_len + 4 + 3 + 1); 185 | strcat(final_string, "{\r\n\t"); 186 | strcat(final_string, all_item); 187 | strcat(final_string, "\r\n}"); 188 | 189 | if(all_item) free(all_item); 190 | printf("\n%s\n", final_string); 191 | dump_to_file(final_string, strlen(final_string), conf_path); 192 | if(final_string) free(final_string); 193 | } 194 | -------------------------------------------------------------------------------- /src/twittrouter.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "twittrouter.h" 9 | #include "utils.h" 10 | #include "jconf.h" 11 | 12 | jconf_t *conf = NULL; 13 | char *root = NULL; 14 | char *servPort = NULL; 15 | linklist arpList = NULL; 16 | 17 | void *ThreadMain(void *arg); // Main program of a thread 18 | // Structure of arguments to pass to client thread 19 | struct ThreadArgs { 20 | int clntSock; // Socket descriptor for client 21 | }; 22 | 23 | static void create_thread(void *thread_func,void *threadArgs) { 24 | pthread_t threadID; 25 | int returnValue = pthread_create(&threadID, NULL, thread_func, threadArgs); 26 | if (returnValue != 0) 27 | FATAL("pthread_create() failed"); 28 | printf("with thread %ld\n", (long int) threadID); 29 | } 30 | 31 | int main(int argc, char *argv[]) { 32 | int auth_flags = 0; 33 | char c; 34 | char *conf_path = DEFAULT_CONFIG_PATH; 35 | char *TwitterID = NULL; 36 | char *userfortest = NULL; 37 | 38 | root = DEFAULT_SERVER_DIR; 39 | servPort = DEFAULT_SERVER_PORT; 40 | 41 | opterr = 0; 42 | 43 | while ((c = getopt (argc, argv, "p:r:c:t:u:f:ha")) != -1) { 44 | switch (c) 45 | { 46 | case 'r': 47 | root = optarg; 48 | break; 49 | case 'p': 50 | servPort = optarg; 51 | break; 52 | case 'c': 53 | conf_path = optarg; 54 | break; 55 | case 'u': 56 | userfortest = optarg; //it's just used for test oauth 57 | break; 58 | case 't': 59 | TwitterID = optarg; //it's just used to test the twitter username display on the webpage 60 | break; 61 | case 'h': 62 | usage(); 63 | exit(EXIT_SUCCESS); 64 | break; 65 | case 'a': 66 | auth_flags = 1; 67 | break; 68 | default: 69 | break; 70 | } 71 | } 72 | 73 | if (opterr) { 74 | usage(); 75 | exit(EXIT_FAILURE); 76 | } 77 | 78 | if (conf_path != NULL) 79 | { 80 | conf = read_jconf(conf_path); 81 | if (TwitterID) conf->TwitterID = TwitterID; 82 | if (conf->TwitterID == NULL || 83 | conf->CONSUMER_KEY == NULL || conf->CONSUMER_SECRET == NULL || 84 | conf->OAUTH_TOKEN == NULL || conf->OAUTH_TOKEN_SECRET == NULL) { 85 | usage(); 86 | exit(EXIT_FAILURE); 87 | } 88 | } 89 | 90 | if(userfortest != NULL) { 91 | if(get_friendship(userfortest)) { 92 | printf("Congratulations! Verify success,%s is your twitter friend\n", userfortest); 93 | } 94 | else { 95 | printf("Sorry! Maybe you have not config your network to go through to evil GFW,or try another friend again\n"); 96 | } 97 | exit(EXIT_SUCCESS); 98 | } 99 | 100 | if (auth_flags) 101 | { 102 | char pin[10] = {0}; 103 | request_token_example_get(); 104 | printf("PIN: "); 105 | scanf("%s", pin); 106 | printf("pin=%s\n", pin); 107 | if(access_token_example_get(pin)) { 108 | printf("Congratulations! auth success\n"); 109 | dump_jconf(conf_path); 110 | } 111 | else { 112 | printf("Sorry! auth failed\n"); 113 | } 114 | exit(EXIT_SUCCESS); 115 | } 116 | 117 | //we need to create a thread to scan arp list and block some ip. 118 | arpList = CreatEmptyLink(); 119 | LOGD("server listening at port %s...\n",servPort); 120 | 121 | int servSock = SetupTCPServerSocket(servPort); 122 | if (servSock < 0) 123 | FATAL("unable to establish"); 124 | 125 | fd_set readset, errorset; 126 | int max_fd = servSock + 1; 127 | int loop_count = 0; 128 | for (;;) { // Run forever 129 | FD_ZERO(&readset); 130 | FD_ZERO(&errorset); 131 | FD_SET(servSock, &readset); 132 | FD_SET(servSock, &errorset); 133 | struct timeval timeout = { 134 | .tv_sec = 1, 135 | .tv_usec = 0, 136 | }; 137 | 138 | if (-1 == select(max_fd, &readset, NULL, &errorset, &timeout)) { 139 | ERROR("select"); 140 | } 141 | 142 | if (FD_ISSET(servSock, &errorset)) { 143 | // TODO getsockopt(..., SO_ERROR, ...); 144 | ERROR("servSock error\n"); 145 | } 146 | 147 | if(FD_ISSET(servSock, &readset)) { 148 | int clntSock = AcceptTCPConnection(servSock); 149 | // Create separate memory for client argument 150 | struct ThreadArgs *threadArgs = (struct ThreadArgs *) malloc( 151 | sizeof(struct ThreadArgs) 152 | ); 153 | if (threadArgs == NULL) 154 | FATAL("malloc() failed"); 155 | threadArgs->clntSock = clntSock; 156 | 157 | // Create client thread 158 | create_thread(ThreadMain,(void *)threadArgs); 159 | } 160 | if(loop_count % 10 == 0){ 161 | int fd; 162 | char buf[BUFSIZE] = {0}; 163 | if ((fd=open("/proc/net/arp", O_RDONLY)) != -1) //FILE FOUND 164 | { 165 | read(fd, buf, BUFSIZE); 166 | scan_arp_and_block(buf); 167 | close(fd); 168 | } 169 | loop_count = 0; 170 | } 171 | loop_count++; 172 | } 173 | // NOT REACHED 174 | } 175 | 176 | void *ThreadMain(void *threadArgs) { 177 | // Guarantees that thread resources are deallocated upon return 178 | pthread_detach(pthread_self()); 179 | 180 | // Extract socket file descriptor from argument 181 | int clntSock = ((struct ThreadArgs *) threadArgs)->clntSock; 182 | free(threadArgs); // Deallocate memory for argument 183 | 184 | HandleTCPClient(clntSock); 185 | return (NULL); 186 | } 187 | -------------------------------------------------------------------------------- /src/json.h: -------------------------------------------------------------------------------- 1 | 2 | /* vim: set et ts=3 sw=3 ft=c: 3 | * 4 | * Copyright (C) 2012 James McLaughlin et al. All rights reserved. 5 | * https://github.com/udp/json-parser 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef _JSON_H 32 | #define _JSON_H 33 | 34 | #ifndef json_char 35 | #define json_char char 36 | #endif 37 | 38 | #ifndef json_int_t 39 | #ifndef _MSC_VER 40 | #include 41 | #define json_int_t int64_t 42 | #else 43 | #define json_int_t __int64 44 | #endif 45 | #endif 46 | 47 | #include 48 | 49 | #ifdef __cplusplus 50 | 51 | #include 52 | 53 | extern "C" 54 | { 55 | 56 | #endif 57 | 58 | typedef struct 59 | { 60 | unsigned long max_memory; 61 | int settings; 62 | 63 | /* Custom allocator support (leave null to use malloc/free) 64 | */ 65 | 66 | void * (* mem_alloc) (size_t, int zero, void * user_data); 67 | void (* mem_free) (void *, void * user_data); 68 | 69 | void * user_data; /* will be passed to mem_alloc and mem_free */ 70 | 71 | } json_settings; 72 | 73 | #define json_relaxed_commas 1 74 | 75 | typedef enum 76 | { 77 | json_none, 78 | json_object, 79 | json_array, 80 | json_integer, 81 | json_double, 82 | json_string, 83 | json_boolean, 84 | json_null 85 | 86 | } json_type; 87 | 88 | extern const struct _json_value json_value_none; 89 | 90 | typedef struct _json_value 91 | { 92 | struct _json_value * parent; 93 | 94 | json_type type; 95 | 96 | union 97 | { 98 | int boolean; 99 | json_int_t integer; 100 | double dbl; 101 | 102 | struct 103 | { 104 | unsigned int length; 105 | json_char * ptr; /* null terminated */ 106 | 107 | } string; 108 | 109 | struct 110 | { 111 | unsigned int length; 112 | 113 | struct 114 | { 115 | json_char * name; 116 | struct _json_value * value; 117 | 118 | } * values; 119 | 120 | } object; 121 | 122 | struct 123 | { 124 | unsigned int length; 125 | struct _json_value ** values; 126 | 127 | } array; 128 | 129 | } u; 130 | 131 | union 132 | { 133 | struct _json_value * next_alloc; 134 | void * object_mem; 135 | 136 | } _reserved; 137 | 138 | 139 | /* Some C++ operator sugar */ 140 | 141 | #ifdef __cplusplus 142 | 143 | public: 144 | 145 | inline _json_value () 146 | { 147 | memset (this, 0, sizeof (_json_value)); 148 | } 149 | 150 | inline const struct _json_value &operator [] (int index) const 151 | { 152 | if (type != json_array || index < 0 153 | || ((unsigned int) index) >= u.array.length) 154 | { 155 | return json_value_none; 156 | } 157 | 158 | return *u.array.values [index]; 159 | } 160 | 161 | inline const struct _json_value &operator [] (const char * index) const 162 | { 163 | if (type != json_object) 164 | return json_value_none; 165 | 166 | for (unsigned int i = 0; i < u.object.length; ++ i) 167 | if (!strcmp (u.object.values [i].name, index)) 168 | return *u.object.values [i].value; 169 | 170 | return json_value_none; 171 | } 172 | 173 | inline operator const char * () const 174 | { 175 | switch (type) 176 | { 177 | case json_string: 178 | return u.string.ptr; 179 | 180 | default: 181 | return ""; 182 | }; 183 | } 184 | 185 | inline operator json_int_t () const 186 | { 187 | switch (type) 188 | { 189 | case json_integer: 190 | return u.integer; 191 | 192 | case json_double: 193 | return (json_int_t) u.dbl; 194 | 195 | default: 196 | return 0; 197 | }; 198 | } 199 | 200 | inline operator bool () const 201 | { 202 | if (type != json_boolean) 203 | return false; 204 | 205 | return u.boolean != 0; 206 | } 207 | 208 | inline operator double () const 209 | { 210 | switch (type) 211 | { 212 | case json_integer: 213 | return (double) u.integer; 214 | 215 | case json_double: 216 | return u.dbl; 217 | 218 | default: 219 | return 0; 220 | }; 221 | } 222 | 223 | #endif 224 | 225 | } json_value; 226 | 227 | json_value * json_parse (const json_char * json, 228 | size_t length); 229 | 230 | json_value * json_parse_ex (json_settings * settings, 231 | const json_char * json, 232 | size_t length, 233 | char * error); 234 | 235 | void json_value_free (json_value *); 236 | 237 | 238 | /* Not usually necessary, unless you used a custom mem_alloc and now want to 239 | * use a custom mem_free. 240 | */ 241 | void json_value_free_ex (json_settings * settings, 242 | json_value *); 243 | 244 | 245 | #ifdef __cplusplus 246 | } /* extern "C" */ 247 | #endif 248 | 249 | #endif 250 | 251 | 252 | -------------------------------------------------------------------------------- /src/twitter.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Main Test and Example Code. 3 | * 4 | * compile: 5 | * gcc -lssl -loauth -lcurl -o twitter twitter.c 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "oauth.h" 14 | #include "xmalloc.h" 15 | #include "jconf.h" 16 | #include "utils.h" 17 | #include "twittrouter.h" 18 | 19 | /* constants */ 20 | const char *request_token_uri = "https://api.twitter.com/oauth/request_token&oauth_callback=oob"; 21 | const char *access_token_uri = "https://api.twitter.com/oauth/access_token"; 22 | const char *authorize_uri = "https://api.twitter.com/oauth/authorize"; 23 | const char *req_c_key = "yo9tIaQs7prILLMSq3DQiQ"; //< consumer key 24 | const char *req_c_secret = "EwOoyEkpb9STlZE6F0HtqofHhcPPhbpUpQel5lWoM"; //< consumer secret 25 | 26 | /* global */ 27 | char *req_t_key = NULL; 28 | char *req_t_secret = NULL; 29 | 30 | 31 | struct MemoryStruct { 32 | char *data; 33 | size_t size; //< bytes remaining (r), bytes accumulated (w) 34 | 35 | size_t start_size; //< only used with ..AndCall() 36 | void (*callback)(void*,int,size_t,size_t); //< only used with ..AndCall() 37 | void *callback_data; //< only used with ..AndCall() 38 | }; 39 | 40 | static size_t 41 | WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data) { 42 | size_t realsize = size * nmemb; 43 | struct MemoryStruct *mem = (struct MemoryStruct *)data; 44 | 45 | mem->data = (char *)xrealloc(mem->data, mem->size + realsize + 1); 46 | if (mem->data) { 47 | memcpy(&(mem->data[mem->size]), ptr, realsize); 48 | mem->size += realsize; 49 | mem->data[mem->size] = 0; 50 | } 51 | return realsize; 52 | } 53 | 54 | static char* curl_http_get(char *req_url) { 55 | char *reply; 56 | CURL *curl; 57 | CURLcode res; 58 | //char *t1=NULL; 59 | struct MemoryStruct chunk; 60 | 61 | chunk.data=NULL; 62 | chunk.size = 0; 63 | 64 | curl_global_init(CURL_GLOBAL_DEFAULT); 65 | curl = curl_easy_init(); 66 | if(curl) { 67 | curl_easy_setopt(curl, CURLOPT_URL, req_url); 68 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); 69 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); 70 | 71 | 72 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); 73 | 74 | 75 | res = curl_easy_perform(curl); 76 | /* Check for errors */ 77 | if(res != CURLE_OK) 78 | fprintf(stderr, "curl_easy_perform() failed: %s\n", 79 | curl_easy_strerror(res)); 80 | 81 | /* always cleanup */ 82 | curl_easy_cleanup(curl); 83 | reply = chunk.data; 84 | 85 | } 86 | curl_global_cleanup(); 87 | return reply; 88 | } 89 | 90 | /* sub routines */ 91 | /* 92 | * a example requesting and parsing a request-token from an OAuth service-provider 93 | * excercising the oauth-HTTP GET function. - it is almost the same as 94 | * \ref request_token_example_post below. 95 | */ 96 | static char* access_token_request_data(char *username) { 97 | char *req_url = NULL; 98 | char *reply; 99 | char friendship_url[MAXSTRINGLENGTH] = {'\0',}; 100 | snprintf(friendship_url, MAXSTRINGLENGTH, "https://api.twitter.com/1.1/friendships/lookup.json?screen_name=%s,%s", conf->TwitterID, username); 101 | req_url = oauth_sign_url2(friendship_url, NULL, OA_HMAC, 102 | NULL, conf->CONSUMER_KEY , conf->CONSUMER_SECRET , 103 | conf->OAUTH_TOKEN , conf->OAUTH_TOKEN_SECRET); 104 | 105 | printf("request URL:%s\n\n", req_url); 106 | reply = curl_http_get(req_url); /* GET */ 107 | 108 | if (!reply) 109 | printf("HTTP request for an oauth request-token failed.\n"); 110 | else { 111 | printf("HTTP-reply: %s\n", reply); 112 | } 113 | 114 | if(req_url) free(req_url); 115 | if(reply) return reply; 116 | else return NULL; 117 | } 118 | 119 | /* sub routines */ 120 | /* 121 | * a example requesting and parsing a request-token from an OAuth service-provider 122 | * excercising the oauth-HTTP GET function. - it is almost the same as 123 | * \ref request_token_example_post below. 124 | */ 125 | void request_token_example_get(void) { 126 | char *req_url = NULL; 127 | char *reply; 128 | 129 | req_url = oauth_sign_url2(request_token_uri, NULL, OA_HMAC, 130 | NULL, conf->CONSUMER_KEY, conf->CONSUMER_SECRET, NULL, NULL); 131 | 132 | printf("request URL:%s\n\n", req_url); 133 | reply = curl_http_get(req_url); /* GET */ 134 | if (!reply) { 135 | printf("Please check your network,maybe it's blocked by GFW\n"); 136 | FATAL("HTTP request for an oauth request-token failed.\n"); 137 | } 138 | else { 139 | int rc; 140 | char **rv = NULL; 141 | 142 | printf("HTTP-reply: %s\n", reply); 143 | rc = oauth_split_url_parameters(reply, &rv); 144 | qsort(rv, rc, sizeof(char *), oauth_cmpstringp); 145 | printf("rc=%d rv[0]=%s rv[1]=%s\n", rc, rv[0], rv[1]); 146 | if(rc>=2) { 147 | int i; 148 | for(i=0; iCONSUMER_KEY, conf->CONSUMER_SECRET, 180 | req_t_key, req_t_secret); 181 | 182 | printf("request URL:%s\n\n", req_url); 183 | 184 | reply = curl_http_get(req_url); /* GET */ 185 | 186 | if (!reply) { 187 | printf("Please check your network,maybe it's blocked by GFW\n"); 188 | FATAL("HTTP request for an oauth request-token failed.\n"); 189 | } 190 | else { 191 | int rc; 192 | char **rv = NULL; 193 | 194 | printf("HTTP-reply: %s\n", reply); 195 | 196 | rc = oauth_split_url_parameters(reply, &rv); 197 | qsort(rv, rc, sizeof(char *), oauth_cmpstringp); 198 | printf("rc=%d rv[0]=%s rv[1]=%s\n", rc, rv[0], rv[1]); 199 | if(rc>=2) { 200 | int i; 201 | for(i=0; iTwitterID) free(conf->TwitterID); 213 | if(conf->OAUTH_TOKEN) free(conf->OAUTH_TOKEN); 214 | if(conf->OAUTH_TOKEN_SECRET) free(conf->OAUTH_TOKEN_SECRET); 215 | conf->TwitterID = screen_name; 216 | conf->OAUTH_TOKEN = res_t_key; 217 | conf->OAUTH_TOKEN_SECRET = res_t_secret; 218 | } 219 | } 220 | 221 | free(rv); 222 | } 223 | 224 | if(req_url) free(req_url); 225 | if(reply) free(reply); 226 | //if(res_t_key) free(res_t_key); 227 | //if(res_t_secret) free(res_t_secret); 228 | //if(screen_name) free(screen_name); 229 | return ret; 230 | } 231 | 232 | 233 | static bool parser_friendship_json(char *friendship) { 234 | if (!friendship) return false; 235 | char *first_front = strchr(friendship,'{'); 236 | char *second_front = strrchr(friendship,'{'); 237 | 238 | if(first_front && first_front != second_front ) { 239 | char *connections = strstr(second_front,"connections"); 240 | if(connections == NULL) { 241 | return false; 242 | } 243 | char *colon = strchr(connections,':'); 244 | if(strncmp(strchr(colon,'"'),"\"none",5) == 0) 245 | return false; 246 | else 247 | return true; 248 | }else { 249 | return false; 250 | } 251 | } 252 | 253 | bool get_friendship(char *username) { 254 | char *friendship = access_token_request_data(username); 255 | if(friendship != NULL && parser_friendship_json(friendship) == true) { 256 | free(friendship); 257 | return true; 258 | } else if(friendship != NULL) { 259 | free(friendship); 260 | } 261 | return false; 262 | } 263 | -------------------------------------------------------------------------------- /src/sha1.c: -------------------------------------------------------------------------------- 1 | /* This code is public-domain - it is based on libcrypt 2 | * placed in the public domain by Wei Dai and other contributors. 3 | */ 4 | // gcc -Wall -DSHA1TEST -o sha1test sha1.c && ./sha1test 5 | 6 | #include 7 | #include 8 | 9 | 10 | #ifdef __BIG_ENDIAN__ 11 | # define SHA_BIG_ENDIAN 12 | #elif defined __LITTLE_ENDIAN__ 13 | /* override */ 14 | #elif defined __BYTE_ORDER 15 | # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 16 | # define SHA_BIG_ENDIAN 17 | # endif 18 | #else // ! defined __LITTLE_ENDIAN__ 19 | # include // machine/endian.h 20 | # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 21 | # define SHA_BIG_ENDIAN 22 | # endif 23 | #endif 24 | 25 | 26 | /* header */ 27 | 28 | #define HASH_LENGTH 20 29 | #define BLOCK_LENGTH 64 30 | 31 | typedef struct sha1nfo { 32 | uint32_t buffer[BLOCK_LENGTH/4]; 33 | uint32_t state[HASH_LENGTH/4]; 34 | uint32_t byteCount; 35 | uint8_t bufferOffset; 36 | uint8_t keyBuffer[BLOCK_LENGTH]; 37 | uint8_t innerHash[HASH_LENGTH]; 38 | } sha1nfo; 39 | 40 | /* public API - prototypes - TODO: doxygen*/ 41 | 42 | /** 43 | */ 44 | void sha1_init(sha1nfo *s); 45 | /** 46 | */ 47 | void sha1_writebyte(sha1nfo *s, uint8_t data); 48 | /** 49 | */ 50 | void sha1_write(sha1nfo *s, const char *data, size_t len); 51 | /** 52 | */ 53 | uint8_t* sha1_result(sha1nfo *s); 54 | /** 55 | */ 56 | void sha1_initHmac(sha1nfo *s, const uint8_t* key, int keyLength); 57 | /** 58 | */ 59 | uint8_t* sha1_resultHmac(sha1nfo *s); 60 | 61 | 62 | /* code */ 63 | #define SHA1_K0 0x5a827999 64 | #define SHA1_K20 0x6ed9eba1 65 | #define SHA1_K40 0x8f1bbcdc 66 | #define SHA1_K60 0xca62c1d6 67 | 68 | void sha1_init(sha1nfo *s) { 69 | s->state[0] = 0x67452301; 70 | s->state[1] = 0xefcdab89; 71 | s->state[2] = 0x98badcfe; 72 | s->state[3] = 0x10325476; 73 | s->state[4] = 0xc3d2e1f0; 74 | s->byteCount = 0; 75 | s->bufferOffset = 0; 76 | } 77 | 78 | uint32_t sha1_rol32(uint32_t number, uint8_t bits) { 79 | return ((number << bits) | (number >> (32-bits))); 80 | } 81 | 82 | void sha1_hashBlock(sha1nfo *s) { 83 | uint8_t i; 84 | uint32_t a,b,c,d,e,t; 85 | 86 | a=s->state[0]; 87 | b=s->state[1]; 88 | c=s->state[2]; 89 | d=s->state[3]; 90 | e=s->state[4]; 91 | for (i=0; i<80; i++) { 92 | if (i>=16) { 93 | t = s->buffer[(i+13)&15] ^ s->buffer[(i+8)&15] ^ s->buffer[(i+2)&15] ^ s->buffer[i&15]; 94 | s->buffer[i&15] = sha1_rol32(t,1); 95 | } 96 | if (i<20) { 97 | t = (d ^ (b & (c ^ d))) + SHA1_K0; 98 | } else if (i<40) { 99 | t = (b ^ c ^ d) + SHA1_K20; 100 | } else if (i<60) { 101 | t = ((b & c) | (d & (b | c))) + SHA1_K40; 102 | } else { 103 | t = (b ^ c ^ d) + SHA1_K60; 104 | } 105 | t+=sha1_rol32(a,5) + e + s->buffer[i&15]; 106 | e=d; 107 | d=c; 108 | c=sha1_rol32(b,30); 109 | b=a; 110 | a=t; 111 | } 112 | s->state[0] += a; 113 | s->state[1] += b; 114 | s->state[2] += c; 115 | s->state[3] += d; 116 | s->state[4] += e; 117 | } 118 | 119 | void sha1_addUncounted(sha1nfo *s, uint8_t data) { 120 | uint8_t * const b = (uint8_t*) s->buffer; 121 | #ifdef SHA_BIG_ENDIAN 122 | b[s->bufferOffset] = data; 123 | #else 124 | b[s->bufferOffset ^ 3] = data; 125 | #endif 126 | s->bufferOffset++; 127 | if (s->bufferOffset == BLOCK_LENGTH) { 128 | sha1_hashBlock(s); 129 | s->bufferOffset = 0; 130 | } 131 | } 132 | 133 | void sha1_writebyte(sha1nfo *s, uint8_t data) { 134 | ++s->byteCount; 135 | sha1_addUncounted(s, data); 136 | } 137 | 138 | void sha1_write(sha1nfo *s, const char *data, size_t len) { 139 | for (;len--;) sha1_writebyte(s, (uint8_t) *data++); 140 | } 141 | 142 | void sha1_pad(sha1nfo *s) { 143 | // Implement SHA-1 padding (fips180-2 §5.1.1) 144 | 145 | // Pad with 0x80 followed by 0x00 until the end of the block 146 | sha1_addUncounted(s, 0x80); 147 | while (s->bufferOffset != 56) sha1_addUncounted(s, 0x00); 148 | 149 | // Append length in the last 8 bytes 150 | sha1_addUncounted(s, 0); // We're only using 32 bit lengths 151 | sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths 152 | sha1_addUncounted(s, 0); // So zero pad the top bits 153 | sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8 154 | sha1_addUncounted(s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as 155 | sha1_addUncounted(s, s->byteCount >> 13); // byte. 156 | sha1_addUncounted(s, s->byteCount >> 5); 157 | sha1_addUncounted(s, s->byteCount << 3); 158 | } 159 | 160 | uint8_t* sha1_result(sha1nfo *s) { 161 | // Pad to complete the last block 162 | sha1_pad(s); 163 | 164 | #ifndef SHA_BIG_ENDIAN 165 | // Swap byte order back 166 | int i; 167 | for (i=0; i<5; i++) { 168 | s->state[i]= 169 | (((s->state[i])<<24)& 0xff000000) 170 | | (((s->state[i])<<8) & 0x00ff0000) 171 | | (((s->state[i])>>8) & 0x0000ff00) 172 | | (((s->state[i])>>24)& 0x000000ff); 173 | } 174 | #endif 175 | 176 | // Return pointer to hash (20 characters) 177 | return (uint8_t*) s->state; 178 | } 179 | 180 | #define HMAC_IPAD 0x36 181 | #define HMAC_OPAD 0x5c 182 | 183 | void sha1_initHmac(sha1nfo *s, const uint8_t* key, int keyLength) { 184 | uint8_t i; 185 | memset(s->keyBuffer, 0, BLOCK_LENGTH); 186 | if (keyLength > BLOCK_LENGTH) { 187 | // Hash long keys 188 | sha1_init(s); 189 | for (;keyLength--;) sha1_writebyte(s, *key++); 190 | memcpy(s->keyBuffer, sha1_result(s), HASH_LENGTH); 191 | } else { 192 | // Block length keys are used as is 193 | memcpy(s->keyBuffer, key, keyLength); 194 | } 195 | // Start inner hash 196 | sha1_init(s); 197 | for (i=0; ikeyBuffer[i] ^ HMAC_IPAD); 199 | } 200 | } 201 | 202 | uint8_t* sha1_resultHmac(sha1nfo *s) { 203 | uint8_t i; 204 | // Complete inner hash 205 | memcpy(s->innerHash,sha1_result(s),HASH_LENGTH); 206 | // Calculate outer hash 207 | sha1_init(s); 208 | for (i=0; ikeyBuffer[i] ^ HMAC_OPAD); 209 | for (i=0; iinnerHash[i]); 210 | return sha1_result(s); 211 | } 212 | 213 | /* self-test */ 214 | 215 | #if SHA1TEST 216 | #include 217 | 218 | uint8_t hmacKey1[]={ 219 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, 220 | 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, 221 | 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, 222 | 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f 223 | }; 224 | uint8_t hmacKey2[]={ 225 | 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, 226 | 0x40,0x41,0x42,0x43 227 | }; 228 | uint8_t hmacKey3[]={ 229 | 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f, 230 | 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f, 231 | 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f, 232 | 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f, 233 | 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f, 234 | 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf, 235 | 0xb0,0xb1,0xb2,0xb3 236 | }; 237 | uint8_t hmacKey4[]={ 238 | 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f, 239 | 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f, 240 | 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f, 241 | 0xa0 242 | }; 243 | 244 | void printHash(uint8_t* hash) { 245 | int i; 246 | for (i=0; i<20; i++) { 247 | printf("%02x", hash[i]); 248 | } 249 | printf("\n"); 250 | } 251 | 252 | 253 | int main (int argc, char **argv) { 254 | uint32_t a; 255 | sha1nfo s; 256 | 257 | // SHA tests 258 | printf("Test: FIPS 180-2 C.1 and RFC3174 7.3 TEST1\n"); 259 | printf("Expect:a9993e364706816aba3e25717850c26c9cd0d89d\n"); 260 | printf("Result:"); 261 | sha1_init(&s); 262 | sha1_write(&s, "abc", 3); 263 | printHash(sha1_result(&s)); 264 | printf("\n\n"); 265 | 266 | printf("Test: FIPS 180-2 C.2 and RFC3174 7.3 TEST2\n"); 267 | printf("Expect:84983e441c3bd26ebaae4aa1f95129e5e54670f1\n"); 268 | printf("Result:"); 269 | sha1_init(&s); 270 | sha1_write(&s, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56); 271 | printHash(sha1_result(&s)); 272 | printf("\n\n"); 273 | 274 | printf("Test: RFC3174 7.3 TEST4\n"); 275 | printf("Expect:dea356a2cddd90c7a7ecedc5ebb563934f460452\n"); 276 | printf("Result:"); 277 | sha1_init(&s); 278 | for (a=0; a<80; a++) sha1_write(&s, "01234567", 8); 279 | printHash(sha1_result(&s)); 280 | printf("\n\n"); 281 | 282 | // HMAC tests 283 | printf("Test: FIPS 198a A.1\n"); 284 | printf("Expect:4f4ca3d5d68ba7cc0a1208c9c61e9c5da0403c0a\n"); 285 | printf("Result:"); 286 | sha1_initHmac(&s, hmacKey1, 64); 287 | sha1_write(&s, "Sample #1",9); 288 | printHash(sha1_resultHmac(&s)); 289 | printf("\n\n"); 290 | 291 | printf("Test: FIPS 198a A.2\n"); 292 | printf("Expect:0922d3405faa3d194f82a45830737d5cc6c75d24\n"); 293 | printf("Result:"); 294 | sha1_initHmac(&s, hmacKey2, 20); 295 | sha1_write(&s, "Sample #2", 9); 296 | printHash(sha1_resultHmac(&s)); 297 | printf("\n\n"); 298 | 299 | printf("Test: FIPS 198a A.3\n"); 300 | printf("Expect:bcf41eab8bb2d802f3d05caf7cb092ecf8d1a3aa\n"); 301 | printf("Result:"); 302 | sha1_initHmac(&s, hmacKey3,100); 303 | sha1_write(&s, "Sample #3", 9); 304 | printHash(sha1_resultHmac(&s)); 305 | printf("\n\n"); 306 | 307 | printf("Test: FIPS 198a A.4\n"); 308 | printf("Expect:9ea886efe268dbecce420c7524df32e0751a2a26\n"); 309 | printf("Result:"); 310 | sha1_initHmac(&s, hmacKey4,49); 311 | sha1_write(&s, "Sample #4", 9); 312 | printHash(sha1_resultHmac(&s)); 313 | printf("\n\n"); 314 | 315 | // Long tests 316 | printf("Test: FIPS 180-2 C.3 and RFC3174 7.3 TEST3\n"); 317 | printf("Expect:34aa973cd4c4daa4f61eeb2bdbad27316534016f\n"); 318 | printf("Result:"); 319 | sha1_init(&s); 320 | for (a=0; a<1000000; a++) sha1_writebyte(&s, 'a'); 321 | printHash(sha1_result(&s)); 322 | 323 | return 0; 324 | } 325 | #endif /* self-test */ 326 | -------------------------------------------------------------------------------- /src/TCPServerUtility.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "twittrouter.h" 11 | #include "jconf.h" 12 | #include "utils.h" 13 | 14 | static const int MAXPENDING = 5; // Maximum outstanding connection requests 15 | static void handle_http_get(int clntSocket, char* file); 16 | 17 | static char* get_twitter_id(int clntSocket, char *poststr, char *user, ssize_t buffer_len) { 18 | if(!poststr) return NULL; 19 | char *content_length = strstr(poststr, "Content-Length"); 20 | if(!content_length) return NULL; 21 | int i = 0; 22 | char content_length_value[TWITTER_USERNAME_MAX_LEN] = {'\0',}; 23 | while(*(content_length + 16 + i) != '\n') { 24 | if(*(content_length + 16 + i) >= '0' && *(content_length + 16 + i) <= '9') { 25 | content_length_value[i] = *(content_length + 16 + i); 26 | } else { 27 | if(*(content_length + 16 + i - 1) >= '0' && *(content_length + 16 + i - 1) <= '9') { 28 | break; 29 | } 30 | } 31 | i++; 32 | } 33 | LOGD("content_length_value = %s",content_length_value); 34 | if (content_length_value[0] == '\0') return NULL; 35 | int length = atoi(content_length_value); 36 | if(length - 6 > TWITTER_USERNAME_MAX_LEN) { 37 | return NULL; 38 | } 39 | 40 | char *username = strstr(poststr,"uname="); 41 | char buffer[BUFSIZE] = {'\0',}; 42 | char *crlf = strstr(poststr,"\r\n\r\n"); 43 | LOGD("first recv length = %d", buffer_len); 44 | if(crlf != NULL && crlf - poststr + 4 + length <= buffer_len && username == NULL) { 45 | LOGD("expect length = %d", crlf - poststr + 4 + length); 46 | return NULL; 47 | } 48 | 49 | if(username == NULL) { 50 | ssize_t numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0); 51 | if (numBytesRcvd <= 0){ 52 | ERROR("recv() failed"); 53 | return NULL; 54 | } 55 | LOGD("get username start"); 56 | LOGD("%s",buffer); 57 | LOGD("get username finished"); 58 | username = strstr(buffer,"uname="); 59 | if(username == NULL) return NULL; 60 | } 61 | 62 | if(length <= 6) { 63 | handle_http_get(clntSocket, "/VERIFY_FAILED.html"); 64 | return NULL; 65 | } 66 | 67 | int j = 0; 68 | char *word = username + 6; 69 | while(j + 6 < length) { 70 | if(*(word + j) == '_' || 71 | (*(word + j) >= '0' && *(word + j) <= '9') || 72 | (*(word + j) >= 'A' && *(word + j) <= 'Z') || 73 | (*(word + j) >= 'a' && *(word + j) <= 'z')) { 74 | user[j] = *(word + j); 75 | j++; 76 | } else { 77 | handle_http_get(clntSocket, "/VERIFY_FAILED.html"); 78 | return NULL; 79 | } 80 | } 81 | 82 | LOGD("a friend of twitter %s is verifying...\n", user); 83 | return user; 84 | } 85 | 86 | static void send_to_client(int clntSocket, char *buffer, int send_byte) { 87 | ssize_t numBytesSent = send(clntSocket, buffer, send_byte, 0); 88 | if (numBytesSent < 0) 89 | ERROR("send() failed"); 90 | } 91 | 92 | static void handle_http_get(int clntSocket, char* file) { 93 | char data_to_send[BUFSIZE]; 94 | char path[128] = {'\0',}; 95 | int bytes_read; 96 | int fd; 97 | 98 | strcpy(path, root); 99 | strcpy(&path[strlen(root)], file); 100 | LOGD("response to client and send file: %s", path); 101 | 102 | if ((fd=open(path, O_RDONLY)) != -1) //FILE FOUND 103 | { 104 | send_to_client(clntSocket, "HTTP/1.0 200 OK\r\n\r\n", 19); //send header 105 | //send_to_client(clntSocket, "Content-type: text/html\n\n", 25); 106 | while ((bytes_read=read(fd, data_to_send, BUFSIZE)) > 0) { 107 | if (strcmp(strrchr(path,'.'), ".html") == 0) { 108 | char *replaced_id = str_replace(data_to_send, "twitterid", conf->TwitterID); 109 | if (replaced_id) { 110 | send_to_client(clntSocket,replaced_id, strstr(replaced_id,"") - replaced_id + 7); 111 | free(replaced_id); 112 | } 113 | 114 | } else { 115 | send_to_client(clntSocket,data_to_send, bytes_read); 116 | } 117 | } 118 | close(fd); 119 | } 120 | else { 121 | send_to_client(clntSocket, "HTTP/1.0 404 Not Found\r\n\r\n", 26); //FILE NOT FOUND 122 | } 123 | } 124 | 125 | static void handle_http_post(int clntSocket, char *username) { 126 | if(get_friendship(username)) { 127 | handle_http_get(clntSocket, "/VERIFY_OK.html"); 128 | sleep(1); 129 | struct sockaddr_storage localAddr; 130 | socklen_t addrSize = sizeof(localAddr); 131 | //char addrBuffer[INET6_ADDRSTRLEN]; 132 | if (getpeername(clntSocket, (struct sockaddr *) &localAddr, &addrSize) < 0) 133 | ERROR("getsockname() failed"); 134 | char *sock_addr = PrintSocketAddress((struct sockaddr *) &localAddr, stdout, 1); 135 | if(sock_addr) { 136 | LOGD("client sock_addr = %s",sock_addr); 137 | 138 | linklist sock_addr_node = Query(arpList,sock_addr); 139 | if(sock_addr_node && sock_addr_node->ipType == BLOCKED_FLAG) { 140 | char iptable_unblock_cmd[MAXSTRINGLENGTH] = {'\0',}; 141 | sprintf(iptable_unblock_cmd,"iptables -t nat -D PREROUTING -s %s -p tcp --dport 80 -j REDIRECT --to-ports %s", sock_addr, servPort); 142 | char *block_cmd_output = exec_cmd_shell(iptable_unblock_cmd); 143 | if(!block_cmd_output) free(block_cmd_output); 144 | LOGD("authed client ip address %s",sock_addr); 145 | Update(sock_addr_node,OAUTHED_FLAG); 146 | } 147 | free(sock_addr); 148 | } 149 | } else { 150 | handle_http_get(clntSocket, "/VERIFY_FAILED.html"); 151 | } 152 | } 153 | 154 | int SetupTCPServerSocket(const char *service) { 155 | // Construct the server address structure 156 | struct addrinfo addrCriteria; // Criteria for address match 157 | memset(&addrCriteria, 0, sizeof(addrCriteria)); // Zero out structure 158 | addrCriteria.ai_family = AF_INET; // Any address family 159 | addrCriteria.ai_flags = AI_PASSIVE; // Accept on any address/port 160 | addrCriteria.ai_socktype = SOCK_STREAM; // Only stream sockets 161 | addrCriteria.ai_protocol = IPPROTO_TCP; // Only TCP protocol 162 | 163 | struct addrinfo *servAddr; // List of server addresses 164 | int rtnVal = getaddrinfo(NULL, service, &addrCriteria, &servAddr); 165 | if (rtnVal != 0) 166 | ERROR("getaddrinfo() failed"); 167 | 168 | int servSock = -1; 169 | for (struct addrinfo *addr = servAddr; addr != NULL; addr = addr->ai_next) { 170 | // Create a TCP socket 171 | servSock = socket(addr->ai_family, addr->ai_socktype, 172 | addr->ai_protocol); 173 | if (servSock < 0) 174 | continue; // Socket creation failed; try next address 175 | 176 | int opt = 1; 177 | setsockopt(servSock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 178 | // Bind to the local address and set socket to listen 179 | if ((bind(servSock, addr->ai_addr, addr->ai_addrlen) == 0) && 180 | (listen(servSock, MAXPENDING) == 0)) { 181 | // Print local address of socket 182 | struct sockaddr_storage localAddr; 183 | socklen_t addrSize = sizeof(localAddr); 184 | //char addrBuffer[INET6_ADDRSTRLEN]; 185 | if (getsockname(servSock, (struct sockaddr *) &localAddr, &addrSize) < 0) 186 | ERROR("getsockname() failed"); 187 | fputs("Binding to ", stdout); 188 | PrintSocketAddress((struct sockaddr *) &localAddr, stdout, 0); 189 | fputc('\n', stdout); 190 | break; // Bind and listen successful 191 | } 192 | 193 | close(servSock); // Close and try again 194 | servSock = -1; 195 | } 196 | 197 | // Free address list allocated by getaddrinfo() 198 | freeaddrinfo(servAddr); 199 | 200 | return servSock; 201 | } 202 | 203 | int AcceptTCPConnection(int servSock) { 204 | struct sockaddr_storage clntAddr; // Client address 205 | // Set length of client address structure (in-out parameter) 206 | socklen_t clntAddrLen = sizeof(clntAddr); 207 | 208 | // Wait for a client to connect 209 | int clntSock = accept(servSock, (struct sockaddr *) &clntAddr, &clntAddrLen); 210 | //char addrBuffer[INET6_ADDRSTRLEN]; 211 | if (clntSock < 0) 212 | ERROR("accept() failed"); 213 | 214 | // clntSock is connected to a client! 215 | 216 | fputs("Handling client ", stdout); 217 | //PrintSocketAddress((struct sockaddr *) &clntAddr, stdout, addrBuffer); 218 | PrintSocketAddress((struct sockaddr *) &clntAddr, stdout,0); 219 | fputc('\n', stdout); 220 | 221 | return clntSock; 222 | } 223 | 224 | void HandleTCPClient(int clntSocket) { 225 | char buffer[BUFSIZE]; // Buffer for echo string 226 | 227 | // Receive message from client 228 | ssize_t numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0); 229 | if (numBytesRcvd <= 0){ 230 | ERROR("recv() failed"); 231 | close(clntSocket); 232 | return; 233 | } 234 | LOGD("first recv start\n"); 235 | printf("%s",buffer); 236 | LOGD("recv finished\n"); 237 | 238 | if (strstr(buffer, "HTTP/1.") == NULL) 239 | { 240 | send(clntSocket, "HTTP/1.0 400 Bad Request\r\n\r\n", 28, 0); 241 | } else if (strncmp(buffer, "GET", 3) == 0) { 242 | char *req_file = strtok (buffer, " \t\n"); 243 | req_file = strtok (NULL, " \t"); 244 | if (req_file == NULL || (strcmp(req_file, "/favicon.ico") && strcmp(req_file, "/twitter-logo-1.png"))) { 245 | req_file = "/BASEHTML.html"; 246 | } 247 | handle_http_get(clntSocket, req_file); 248 | } else if (strncmp(buffer, "POST / ", 7) == 0){ 249 | char friend[TWITTER_USERNAME_MAX_LEN] = {'\0',}; 250 | char *friend_id = get_twitter_id(clntSocket, buffer, friend, numBytesRcvd); 251 | if(friend_id) 252 | handle_http_post(clntSocket, friend_id); 253 | } 254 | 255 | shutdown(clntSocket, SHUT_RDWR); //All further send and recieve operations are DISABLED... 256 | close(clntSocket); // Close client socket 257 | } 258 | -------------------------------------------------------------------------------- /src/hash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * hash algorithms used in OAuth 3 | * 4 | * Copyright 2007-2012 Robin Gareus 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | //#if HAVE_CONFIG_H 27 | # include "config.h" 28 | //#endif 29 | 30 | #if USE_BUILTIN_HASH // built-in / AVR -- TODO: check license of sha1.c 31 | #include 32 | #include "oauth.h" // oauth_encode_base64 33 | #include "xmalloc.h" 34 | 35 | #include "sha1.c" // TODO: sha1.h ; Makefile.am: add sha1.c 36 | 37 | /* API */ 38 | char *oauth_sign_hmac_sha1_raw (const char *m, const size_t ml, const char *k, const size_t kl) { 39 | sha1nfo s; 40 | sha1_initHmac(&s, (const uint8_t*) k, kl); 41 | sha1_write(&s, m, ml); 42 | unsigned char *digest = sha1_resultHmac(&s); 43 | return oauth_encode_base64(HASH_LENGTH, digest); 44 | } 45 | 46 | char *oauth_sign_hmac_sha1 (const char *m, const char *k) { 47 | return(oauth_sign_hmac_sha1_raw (m, strlen(m), k, strlen(k))); 48 | } 49 | 50 | char *oauth_body_hash_file(char *filename) { 51 | FILE *F= fopen(filename, "r"); 52 | if (!F) return NULL; 53 | 54 | size_t len=0; 55 | char fb[BUFSIZ]; 56 | sha1nfo s; 57 | sha1_init(&s); 58 | 59 | while (!feof(F) && (len=fread(fb,sizeof(char),BUFSIZ, F))>0) { 60 | sha1_write(&s, fb, len); 61 | } 62 | fclose(F); 63 | 64 | unsigned char *dgst = xmalloc(HASH_LENGTH*sizeof(char)); // oauth_body_hash_encode frees the digest.. 65 | memcpy(dgst, sha1_result(&s), HASH_LENGTH); 66 | return oauth_body_hash_encode(HASH_LENGTH, dgst); 67 | } 68 | 69 | char *oauth_body_hash_data(size_t length, const char *data) { 70 | sha1nfo s; 71 | sha1_init(&s); 72 | for (;length--;) sha1_writebyte(&s, *data++); 73 | 74 | unsigned char *dgst = xmalloc(HASH_LENGTH*sizeof(char)); // oauth_body_hash_encode frees the digest.. 75 | memcpy(dgst, sha1_result(&s), HASH_LENGTH); 76 | return oauth_body_hash_encode(HASH_LENGTH, dgst); 77 | } 78 | 79 | char *oauth_sign_rsa_sha1 (const char *m, const char *k) { 80 | /* NOT RSA/PK11 support */ 81 | return xstrdup("---RSA/PK11-is-not-supported-by-this-version-of-liboauth---"); 82 | } 83 | 84 | int oauth_verify_rsa_sha1 (const char *m, const char *c, const char *sig) { 85 | /* NOT RSA/PK11 support */ 86 | return -1; // mismatch , error 87 | } 88 | 89 | #elif defined (USE_NSS) 90 | /* use http://www.mozilla.org/projects/security/pki/nss/ for hash/sign */ 91 | 92 | #include 93 | #include 94 | #include 95 | #include "xmalloc.h" 96 | #include "oauth.h" // oauth base64 encode fn's. 97 | 98 | // NSS includes 99 | #include "pk11pub.h" 100 | #include "nss.h" 101 | #include "base64.h" 102 | #include "keyhi.h" 103 | #include "cryptohi.h" 104 | #include "cert.h" 105 | 106 | #if 1 // work-around compiler-warning 107 | // see http://bugzilla.mozilla.org/show_bug.cgi?id=243245#c3 108 | extern CERTCertificate * 109 | __CERT_DecodeDERCertificate (SECItem *derSignedCert, PRBool copyDER, char *nickname); 110 | #endif 111 | 112 | static const char NS_CERT_HEADER[] = "-----BEGIN CERTIFICATE-----"; 113 | static const char NS_CERT_TRAILER[] = "-----END CERTIFICATE-----"; 114 | static const char NS_PRIV_HEADER[] = "-----BEGIN PRIVATE KEY-----"; 115 | static const char NS_PRIV_TRAILER[] = "-----END PRIVATE KEY-----"; 116 | 117 | void oauth_init_nss() { 118 | static short nss_initialized = 0; 119 | if (!nss_initialized) { NSS_NoDB_Init(NULL); nss_initialized=1;} 120 | } 121 | 122 | /** 123 | * Removes heading & trailing strings; used only internally. 124 | * similar to NSS-source/nss/lib/pkcs7/certread.c 125 | * 126 | * the returned string (if not NULL) needs to be freed by the caller 127 | */ 128 | char *oauth_strip_pkcs(const char *txt, const char *h, const char *t) { 129 | char *start, *end, *rv; 130 | size_t len; 131 | if ((start=strstr(txt, h))==NULL) return NULL; 132 | start+=strlen(h); 133 | while (*start=='\r' || *start=='\n') start++; 134 | if ((end=strstr(start, t))==NULL) return NULL; 135 | end--; 136 | while (*end=='\r' || *end=='\n') end--; 137 | len = end-start+2; 138 | rv = xmalloc(len*sizeof(char)); 139 | memcpy(rv,start,len); 140 | rv[len-1]='\0'; 141 | return rv; 142 | } 143 | 144 | char *oauth_sign_hmac_sha1 (const char *m, const char *k) { 145 | return(oauth_sign_hmac_sha1_raw (m, strlen(m), k, strlen(k))); 146 | } 147 | 148 | char *oauth_sign_hmac_sha1_raw (const char *m, const size_t ml, const char *k, const size_t kl) { 149 | PK11SlotInfo *slot = NULL; 150 | PK11SymKey *pkey = NULL; 151 | PK11Context *context = NULL; 152 | unsigned char digest[20]; // Is there a way to tell how large the output is? 153 | unsigned int len; 154 | SECStatus s; 155 | SECItem keyItem, noParams; 156 | char *rv=NULL; 157 | 158 | keyItem.type = siBuffer; 159 | keyItem.data = (unsigned char*) k; 160 | keyItem.len = kl; 161 | 162 | noParams.type = siBuffer; 163 | noParams.data = NULL; 164 | noParams.len = 0; 165 | 166 | oauth_init_nss(); 167 | 168 | slot = PK11_GetInternalKeySlot(); 169 | if (!slot) goto looser; 170 | pkey = PK11_ImportSymKey(slot, CKM_SHA_1_HMAC, PK11_OriginUnwrap, CKA_SIGN, &keyItem, NULL); 171 | if (!pkey) goto looser; 172 | context = PK11_CreateContextBySymKey(CKM_SHA_1_HMAC, CKA_SIGN, pkey, &noParams); 173 | if (!context) goto looser; 174 | 175 | s = PK11_DigestBegin(context); 176 | if (s != SECSuccess) goto looser; 177 | s = PK11_DigestOp(context, (unsigned char*) m, ml); 178 | if (s != SECSuccess) goto looser; 179 | s = PK11_DigestFinal(context, digest, &len, sizeof digest); 180 | if (s != SECSuccess) goto looser; 181 | 182 | rv=oauth_encode_base64(len, digest); 183 | 184 | looser: 185 | if (context) PK11_DestroyContext(context, PR_TRUE); 186 | if (pkey) PK11_FreeSymKey(pkey); 187 | if (slot) PK11_FreeSlot(slot); 188 | return rv; 189 | } 190 | 191 | char *oauth_sign_rsa_sha1 (const char *m, const char *k) { 192 | PK11SlotInfo *slot = NULL; 193 | SECKEYPrivateKey *pkey = NULL; 194 | SECItem signature; 195 | SECStatus s; 196 | SECItem der; 197 | char *rv=NULL; 198 | 199 | char *key = oauth_strip_pkcs(k, NS_PRIV_HEADER, NS_PRIV_TRAILER); 200 | if (!key) return NULL; 201 | 202 | oauth_init_nss(); 203 | 204 | slot = PK11_GetInternalKeySlot(); 205 | if (!slot) goto looser; 206 | s = ATOB_ConvertAsciiToItem(&der, key); 207 | if (s != SECSuccess) goto looser; 208 | s = PK11_ImportDERPrivateKeyInfoAndReturnKey(slot, &der, NULL, NULL, PR_FALSE, PR_TRUE, KU_ALL, &pkey, NULL); 209 | SECITEM_FreeItem(&der, PR_FALSE); 210 | if (s != SECSuccess) goto looser; 211 | if (!pkey) goto looser; 212 | if (pkey->keyType != rsaKey) goto looser; 213 | s = SEC_SignData(&signature, (unsigned char*) m, strlen(m), pkey, SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE); 214 | if (s != SECSuccess) goto looser; 215 | 216 | rv=oauth_encode_base64(signature.len, signature.data); 217 | SECITEM_FreeItem(&signature, PR_FALSE); 218 | 219 | looser: 220 | if (pkey) SECKEY_DestroyPrivateKey(pkey); 221 | if (slot) PK11_FreeSlot(slot); 222 | free(key); 223 | return rv; 224 | } 225 | 226 | int oauth_verify_rsa_sha1 (const char *m, const char *c, const char *sig) { 227 | PK11SlotInfo *slot = NULL; 228 | SECKEYPublicKey *pkey = NULL; 229 | CERTCertificate *cert = NULL; 230 | SECItem signature; 231 | SECStatus s; 232 | SECItem der; 233 | int rv=0; 234 | 235 | char *key = oauth_strip_pkcs(c, NS_CERT_HEADER, NS_CERT_TRAILER); 236 | if (!key) return 0; 237 | 238 | oauth_init_nss(); 239 | 240 | s = ATOB_ConvertAsciiToItem(&signature, (char*) sig); // XXX cast (const char*) -> (char*) 241 | if (s != SECSuccess) goto looser; 242 | slot = PK11_GetInternalKeySlot(); 243 | if (!slot) goto looser; 244 | s = ATOB_ConvertAsciiToItem(&der, key); 245 | if (s != SECSuccess) goto looser; 246 | cert = __CERT_DecodeDERCertificate(&der, PR_TRUE, NULL); 247 | SECITEM_FreeItem(&der, PR_FALSE); 248 | if (!cert) goto looser; 249 | pkey = CERT_ExtractPublicKey(cert); 250 | if (!pkey) goto looser; 251 | if (pkey->keyType != rsaKey) goto looser; 252 | 253 | s = VFY_VerifyData((unsigned char*) m, strlen(m), pkey, &signature, SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE, NULL); 254 | if (s == SECSuccess) rv=1; 255 | #if 0 256 | else if (PR_GetError()!= SEC_ERROR_BAD_SIGNATURE) rv=-1; 257 | #endif 258 | 259 | looser: 260 | if (pkey) SECKEY_DestroyPublicKey(pkey); 261 | if (slot) PK11_FreeSlot(slot); 262 | free(key); 263 | return rv; 264 | } 265 | 266 | char *oauth_body_hash_file(char *filename) { 267 | PK11SlotInfo *slot = NULL; 268 | PK11Context *context = NULL; 269 | unsigned char digest[20]; // Is there a way to tell how large the output is? 270 | unsigned int len; 271 | SECStatus s; 272 | char *rv=NULL; 273 | size_t bl; 274 | unsigned char fb[BUFSIZ]; 275 | 276 | FILE *F= fopen(filename, "r"); 277 | if (!F) return NULL; 278 | 279 | oauth_init_nss(); 280 | 281 | slot = PK11_GetInternalKeySlot(); 282 | if (!slot) goto looser; 283 | context = PK11_CreateDigestContext(SEC_OID_SHA1); 284 | if (!context) goto looser; 285 | 286 | s = PK11_DigestBegin(context); 287 | if (s != SECSuccess) goto looser; 288 | while (!feof(F) && (bl=fread(fb,sizeof(char),BUFSIZ, F))>0) { 289 | s = PK11_DigestOp(context, (unsigned char*) fb, bl); 290 | if (s != SECSuccess) goto looser; 291 | } 292 | s = PK11_DigestFinal(context, digest, &len, sizeof digest); 293 | if (s != SECSuccess) goto looser; 294 | 295 | unsigned char *dgst = xmalloc(len*sizeof(char)); // oauth_body_hash_encode frees the digest.. 296 | memcpy(dgst, digest, len); 297 | rv=oauth_body_hash_encode(len, dgst); 298 | 299 | looser: 300 | fclose(F); 301 | if (context) PK11_DestroyContext(context, PR_TRUE); 302 | if (slot) PK11_FreeSlot(slot); 303 | return rv; 304 | } 305 | 306 | char *oauth_body_hash_data(size_t length, const char *data) { 307 | PK11SlotInfo *slot = NULL; 308 | PK11Context *context = NULL; 309 | unsigned char digest[20]; // Is there a way to tell how large the output is? 310 | unsigned int len; 311 | SECStatus s; 312 | char *rv=NULL; 313 | 314 | oauth_init_nss(); 315 | 316 | slot = PK11_GetInternalKeySlot(); 317 | if (!slot) goto looser; 318 | context = PK11_CreateDigestContext(SEC_OID_SHA1); 319 | if (!context) goto looser; 320 | 321 | s = PK11_DigestBegin(context); 322 | if (s != SECSuccess) goto looser; 323 | s = PK11_DigestOp(context, (unsigned char*) data, length); 324 | if (s != SECSuccess) goto looser; 325 | s = PK11_DigestFinal(context, digest, &len, sizeof digest); 326 | if (s != SECSuccess) goto looser; 327 | 328 | unsigned char *dgst = xmalloc(len*sizeof(char)); // oauth_body_hash_encode frees the digest.. 329 | memcpy(dgst, digest, len); 330 | rv=oauth_body_hash_encode(len, dgst); 331 | 332 | looser: 333 | if (context) PK11_DestroyContext(context, PR_TRUE); 334 | if (slot) PK11_FreeSlot(slot); 335 | return rv; 336 | } 337 | 338 | #else 339 | /* use http://www.openssl.org/ for hash/sign */ 340 | 341 | #ifdef _GNU_SOURCE 342 | /* 343 | * In addition, as a special exception, the copyright holders give 344 | * permission to link the code of portions of this program with the 345 | * OpenSSL library under certain conditions as described in each 346 | * individual source file, and distribute linked combinations 347 | * including the two. 348 | * You must obey the GNU General Public License in all respects 349 | * for all of the code used other than OpenSSL. If you modify 350 | * file(s) with this exception, you may extend this exception to your 351 | * version of the file(s), but you are not obligated to do so. If you 352 | * do not wish to do so, delete this exception statement from your 353 | * version. If you delete this exception statement from all source 354 | * files in the program, then also delete it here. 355 | */ 356 | #endif 357 | 358 | #include 359 | #include 360 | #include 361 | #include "xmalloc.h" 362 | #include "oauth.h" // base64 encode fn's. 363 | #include 364 | 365 | char *oauth_sign_hmac_sha1 (const char *m, const char *k) { 366 | return(oauth_sign_hmac_sha1_raw (m, strlen(m), k, strlen(k))); 367 | } 368 | 369 | char *oauth_sign_hmac_sha1_raw (const char *m, const size_t ml, const char *k, const size_t kl) { 370 | unsigned char result[EVP_MAX_MD_SIZE]; 371 | unsigned int resultlen = 0; 372 | 373 | HMAC(EVP_sha1(), k, kl, 374 | (unsigned char*) m, ml, 375 | result, &resultlen); 376 | 377 | return(oauth_encode_base64(resultlen, result)); 378 | } 379 | 380 | #include 381 | #include 382 | #include 383 | #include 384 | 385 | char *oauth_sign_rsa_sha1 (const char *m, const char *k) { 386 | unsigned char *sig = NULL; 387 | unsigned char *passphrase = NULL; 388 | unsigned int len=0; 389 | EVP_MD_CTX md_ctx; 390 | 391 | EVP_PKEY *pkey; 392 | BIO *in; 393 | in = BIO_new_mem_buf((unsigned char*) k, strlen(k)); 394 | pkey = PEM_read_bio_PrivateKey(in, NULL, 0, passphrase); // generate sign 395 | BIO_free(in); 396 | 397 | if (pkey == NULL) { 398 | //fprintf(stderr, "liboauth/OpenSSL: can not read private key\n"); 399 | return xstrdup("liboauth/OpenSSL: can not read private key"); 400 | } 401 | 402 | len = EVP_PKEY_size(pkey); 403 | sig = (unsigned char*)xmalloc((len+1)*sizeof(char)); 404 | 405 | EVP_SignInit(&md_ctx, EVP_sha1()); 406 | EVP_SignUpdate(&md_ctx, m, strlen(m)); 407 | if (EVP_SignFinal (&md_ctx, sig, &len, pkey)) { 408 | char *tmp; 409 | sig[len] = '\0'; 410 | tmp = oauth_encode_base64(len,sig); 411 | OPENSSL_free(sig); 412 | EVP_PKEY_free(pkey); 413 | return tmp; 414 | } 415 | return xstrdup("liboauth/OpenSSL: rsa-sha1 signing failed"); 416 | } 417 | 418 | int oauth_verify_rsa_sha1 (const char *m, const char *c, const char *s) { 419 | EVP_MD_CTX md_ctx; 420 | EVP_PKEY *pkey; 421 | BIO *in; 422 | X509 *cert = NULL; 423 | unsigned char *b64d; 424 | int slen, err; 425 | 426 | in = BIO_new_mem_buf((unsigned char*)c, strlen(c)); 427 | cert = PEM_read_bio_X509(in, NULL, 0, NULL); 428 | if (cert) { 429 | pkey = (EVP_PKEY *) X509_get_pubkey(cert); 430 | X509_free(cert); 431 | } else { 432 | pkey = PEM_read_bio_PUBKEY(in, NULL, 0, NULL); 433 | } 434 | BIO_free(in); 435 | if (pkey == NULL) { 436 | //fprintf(stderr, "could not read cert/pubkey.\n"); 437 | return -2; 438 | } 439 | 440 | b64d= (unsigned char*) xmalloc(sizeof(char)*strlen(s)); 441 | slen = oauth_decode_base64(b64d, s); 442 | 443 | EVP_VerifyInit(&md_ctx, EVP_sha1()); 444 | EVP_VerifyUpdate(&md_ctx, m, strlen(m)); 445 | err = EVP_VerifyFinal(&md_ctx, b64d, slen, pkey); 446 | EVP_MD_CTX_cleanup(&md_ctx); 447 | EVP_PKEY_free(pkey); 448 | free(b64d); 449 | return (err); 450 | } 451 | 452 | 453 | /** 454 | * http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html 455 | */ 456 | char *oauth_body_hash_file(char *filename) { 457 | unsigned char fb[BUFSIZ]; 458 | EVP_MD_CTX ctx; 459 | size_t len=0; 460 | unsigned char *md; 461 | FILE *F= fopen(filename, "r"); 462 | if (!F) return NULL; 463 | 464 | EVP_MD_CTX_init(&ctx); 465 | EVP_DigestInit(&ctx,EVP_sha1()); 466 | while (!feof(F) && (len=fread(fb,sizeof(char),BUFSIZ, F))>0) { 467 | EVP_DigestUpdate(&ctx, fb, len); 468 | } 469 | fclose(F); 470 | len=0; 471 | md=(unsigned char*) xcalloc(EVP_MD_size(EVP_sha1()),sizeof(unsigned char)); 472 | EVP_DigestFinal(&ctx, md,(unsigned int*) &len); 473 | EVP_MD_CTX_cleanup(&ctx); 474 | return oauth_body_hash_encode(len, md); 475 | } 476 | 477 | char *oauth_body_hash_data(size_t length, const char *data) { 478 | EVP_MD_CTX ctx; 479 | size_t len=0; 480 | unsigned char *md; 481 | md=(unsigned char*) xcalloc(EVP_MD_size(EVP_sha1()),sizeof(unsigned char)); 482 | EVP_MD_CTX_init(&ctx); 483 | EVP_DigestInit(&ctx,EVP_sha1()); 484 | EVP_DigestUpdate(&ctx, data, length); 485 | EVP_DigestFinal(&ctx, md,(unsigned int*) &len); 486 | EVP_MD_CTX_cleanup(&ctx); 487 | return oauth_body_hash_encode(len, md); 488 | } 489 | 490 | #endif 491 | 492 | // vi: sts=2 sw=2 ts=2 493 | -------------------------------------------------------------------------------- /src/json.c: -------------------------------------------------------------------------------- 1 | 2 | /* vim: set et ts=3 sw=3 ft=c: 3 | * 4 | * Copyright (C) 2012 James McLaughlin et al. All rights reserved. 5 | * https://github.com/udp/json-parser 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include "json.h" 32 | 33 | #ifdef _MSC_VER 34 | #ifndef _CRT_SECURE_NO_WARNINGS 35 | #define _CRT_SECURE_NO_WARNINGS 36 | #endif 37 | #endif 38 | 39 | #ifdef __cplusplus 40 | const struct _json_value json_value_none; /* zero-d by ctor */ 41 | #else 42 | const struct _json_value json_value_none = { 0 }; 43 | #endif 44 | 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | typedef unsigned short json_uchar; 51 | 52 | static unsigned char hex_value (json_char c) 53 | { 54 | if (c >= 'A' && c <= 'F') 55 | return (c - 'A') + 10; 56 | 57 | if (c >= 'a' && c <= 'f') 58 | return (c - 'a') + 10; 59 | 60 | if (c >= '0' && c <= '9') 61 | return c - '0'; 62 | 63 | return 0xFF; 64 | } 65 | 66 | typedef struct 67 | { 68 | unsigned long used_memory; 69 | 70 | unsigned int uint_max; 71 | unsigned long ulong_max; 72 | 73 | json_settings settings; 74 | int first_pass; 75 | 76 | } json_state; 77 | 78 | static void * default_alloc (size_t size, int zero, void * user_data) 79 | { 80 | return zero ? calloc (size, 1) : malloc (size); 81 | } 82 | 83 | static void default_free (void * ptr, void * user_data) 84 | { 85 | free (ptr); 86 | } 87 | 88 | static void * json_alloc (json_state * state, unsigned long size, int zero) 89 | { 90 | if ((state->ulong_max - state->used_memory) < size) 91 | return 0; 92 | 93 | if (state->settings.max_memory 94 | && (state->used_memory += size) > state->settings.max_memory) 95 | { 96 | return 0; 97 | } 98 | 99 | return state->settings.mem_alloc (size, zero, state->settings.user_data); 100 | } 101 | 102 | static int new_value 103 | (json_state * state, json_value ** top, json_value ** root, json_value ** alloc, json_type type) 104 | { 105 | json_value * value; 106 | int values_size; 107 | 108 | if (!state->first_pass) 109 | { 110 | value = *top = *alloc; 111 | *alloc = (*alloc)->_reserved.next_alloc; 112 | 113 | if (!*root) 114 | *root = value; 115 | 116 | switch (value->type) 117 | { 118 | case json_array: 119 | 120 | if (! (value->u.array.values = (json_value **) json_alloc 121 | (state, value->u.array.length * sizeof (json_value *), 0)) ) 122 | { 123 | return 0; 124 | } 125 | 126 | value->u.array.length = 0; 127 | break; 128 | 129 | case json_object: 130 | 131 | values_size = sizeof (*value->u.object.values) * value->u.object.length; 132 | 133 | if (! ((*(void **) &value->u.object.values) = json_alloc 134 | (state, values_size + ((unsigned long) value->u.object.values), 0)) ) 135 | { 136 | return 0; 137 | } 138 | 139 | value->_reserved.object_mem = (*(char **) &value->u.object.values) + values_size; 140 | 141 | value->u.object.length = 0; 142 | break; 143 | 144 | case json_string: 145 | 146 | if (! (value->u.string.ptr = (json_char *) json_alloc 147 | (state, (value->u.string.length + 1) * sizeof (json_char), 0)) ) 148 | { 149 | return 0; 150 | } 151 | 152 | value->u.string.length = 0; 153 | break; 154 | 155 | default: 156 | break; 157 | }; 158 | 159 | return 1; 160 | } 161 | 162 | value = (json_value *) json_alloc (state, sizeof (json_value), 1); 163 | 164 | if (!value) 165 | return 0; 166 | 167 | if (!*root) 168 | *root = value; 169 | 170 | value->type = type; 171 | value->parent = *top; 172 | 173 | if (*alloc) 174 | (*alloc)->_reserved.next_alloc = value; 175 | 176 | *alloc = *top = value; 177 | 178 | return 1; 179 | } 180 | 181 | #define e_off \ 182 | ((int) (i - cur_line_begin)) 183 | 184 | #define whitespace \ 185 | case '\n': ++ cur_line; cur_line_begin = i; \ 186 | case ' ': case '\t': case '\r' 187 | 188 | #define string_add(b) \ 189 | do { if (!state.first_pass) string [string_length] = b; ++ string_length; } while (0); 190 | 191 | const static long 192 | flag_next = 1, flag_reproc = 2, flag_need_comma = 4, flag_seek_value = 8, 193 | flag_escaped = 16, flag_string = 32, flag_need_colon = 64, flag_done = 128, 194 | flag_num_negative = 256, flag_num_zero = 512, flag_num_e = 1024, 195 | flag_num_e_got_sign = 2048, flag_num_e_negative = 4096; 196 | 197 | json_value * json_parse_ex (json_settings * settings, 198 | const json_char * json, 199 | size_t length, 200 | char * error_buf) 201 | { 202 | json_char error [128]; 203 | unsigned int cur_line; 204 | const json_char * cur_line_begin, * i, * end; 205 | json_value * top, * root, * alloc = 0; 206 | json_state state = { 0 }; 207 | long flags; 208 | long num_digits, num_e; 209 | json_int_t num_fraction; 210 | 211 | error[0] = '\0'; 212 | end = (json + length); 213 | 214 | memcpy (&state.settings, settings, sizeof (json_settings)); 215 | 216 | if (!state.settings.mem_alloc) 217 | state.settings.mem_alloc = default_alloc; 218 | 219 | if (!state.settings.mem_free) 220 | state.settings.mem_free = default_free; 221 | 222 | memset (&state.uint_max, 0xFF, sizeof (state.uint_max)); 223 | memset (&state.ulong_max, 0xFF, sizeof (state.ulong_max)); 224 | 225 | state.uint_max -= 8; /* limit of how much can be added before next check */ 226 | state.ulong_max -= 8; 227 | 228 | for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass) 229 | { 230 | json_uchar uchar; 231 | unsigned char uc_b1, uc_b2, uc_b3, uc_b4; 232 | json_char * string; 233 | unsigned int string_length; 234 | 235 | top = root = 0; 236 | flags = flag_seek_value; 237 | 238 | cur_line = 1; 239 | cur_line_begin = json; 240 | 241 | for (i = json ;; ++ i) 242 | { 243 | json_char b = (json == end ? 0 : *i); 244 | 245 | if (flags & flag_done) 246 | { 247 | if (!b) 248 | break; 249 | 250 | switch (b) 251 | { 252 | whitespace: 253 | continue; 254 | 255 | default: 256 | sprintf (error, "%d:%d: Trailing garbage: `%c`", cur_line, e_off, b); 257 | goto e_failed; 258 | }; 259 | } 260 | 261 | if (flags & flag_string) 262 | { 263 | if (!b) 264 | { 265 | sprintf (error, "Unexpected EOF in string (at %d:%d)", cur_line, e_off); 266 | goto e_failed; 267 | } 268 | 269 | if (string_length > state.uint_max) 270 | goto e_overflow; 271 | 272 | if (flags & flag_escaped) 273 | { 274 | flags &= ~ flag_escaped; 275 | 276 | switch (b) 277 | { 278 | case 'b': 279 | string_add ('\b'); 280 | break; 281 | case 'f': 282 | string_add ('\f'); 283 | break; 284 | case 'n': 285 | string_add ('\n'); 286 | break; 287 | case 'r': 288 | string_add ('\r'); 289 | break; 290 | case 't': 291 | string_add ('\t'); 292 | break; 293 | case 'u': 294 | 295 | if ((uc_b1 = hex_value (*++ i)) == 0xFF || (uc_b2 = hex_value (*++ i)) == 0xFF 296 | || (uc_b3 = hex_value (*++ i)) == 0xFF || (uc_b4 = hex_value (*++ i)) == 0xFF) 297 | { 298 | sprintf (error, "Invalid character value `%c` (at %d:%d)", b, cur_line, e_off); 299 | goto e_failed; 300 | } 301 | 302 | uc_b1 = uc_b1 * 16 + uc_b2; 303 | uc_b2 = uc_b3 * 16 + uc_b4; 304 | 305 | uchar = ((json_char) uc_b1) * 256 + uc_b2; 306 | 307 | if (sizeof (json_char) >= sizeof (json_uchar) || (uc_b1 == 0 && uc_b2 <= 0x7F)) 308 | { 309 | string_add ((json_char) uchar); 310 | break; 311 | } 312 | 313 | if (uchar <= 0x7FF) 314 | { 315 | if (state.first_pass) 316 | string_length += 2; 317 | else 318 | { 319 | string [string_length ++] = 0xC0 | ((uc_b2 & 0xC0) >> 6) | ((uc_b1 & 0x7) << 2); 320 | string [string_length ++] = 0x80 | (uc_b2 & 0x3F); 321 | } 322 | 323 | break; 324 | } 325 | 326 | if (state.first_pass) 327 | string_length += 3; 328 | else 329 | { 330 | string [string_length ++] = 0xE0 | ((uc_b1 & 0xF0) >> 4); 331 | string [string_length ++] = 0x80 | ((uc_b1 & 0xF) << 2) | ((uc_b2 & 0xC0) >> 6); 332 | string [string_length ++] = 0x80 | (uc_b2 & 0x3F); 333 | } 334 | 335 | break; 336 | 337 | default: 338 | string_add (b); 339 | }; 340 | 341 | continue; 342 | } 343 | 344 | if (b == '\\') 345 | { 346 | flags |= flag_escaped; 347 | continue; 348 | } 349 | 350 | if (b == '"') 351 | { 352 | if (!state.first_pass) 353 | string [string_length] = 0; 354 | 355 | flags &= ~ flag_string; 356 | string = 0; 357 | 358 | switch (top->type) 359 | { 360 | case json_string: 361 | 362 | top->u.string.length = string_length; 363 | flags |= flag_next; 364 | 365 | break; 366 | 367 | case json_object: 368 | 369 | if (state.first_pass) 370 | (*(json_char **) &top->u.object.values) += string_length + 1; 371 | else 372 | { 373 | top->u.object.values [top->u.object.length].name 374 | = (json_char *) top->_reserved.object_mem; 375 | 376 | (*(json_char **) &top->_reserved.object_mem) += string_length + 1; 377 | } 378 | 379 | flags |= flag_seek_value | flag_need_colon; 380 | continue; 381 | 382 | default: 383 | break; 384 | }; 385 | } 386 | else 387 | { 388 | string_add (b); 389 | continue; 390 | } 391 | } 392 | 393 | if (flags & flag_seek_value) 394 | { 395 | switch (b) 396 | { 397 | whitespace: 398 | continue; 399 | 400 | case ']': 401 | 402 | if (top->type == json_array) 403 | flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next; 404 | else if (!state.settings.settings & json_relaxed_commas) 405 | { 406 | sprintf (error, "%d:%d: Unexpected ]", cur_line, e_off); 407 | goto e_failed; 408 | } 409 | 410 | break; 411 | 412 | default: 413 | 414 | if (flags & flag_need_comma) 415 | { 416 | if (b == ',') 417 | { 418 | flags &= ~ flag_need_comma; 419 | continue; 420 | } 421 | else 422 | { 423 | sprintf (error, "%d:%d: Expected , before %c", cur_line, e_off, b); 424 | goto e_failed; 425 | } 426 | } 427 | 428 | if (flags & flag_need_colon) 429 | { 430 | if (b == ':') 431 | { 432 | flags &= ~ flag_need_colon; 433 | continue; 434 | } 435 | else 436 | { 437 | sprintf (error, "%d:%d: Expected : before %c", cur_line, e_off, b); 438 | goto e_failed; 439 | } 440 | } 441 | 442 | flags &= ~ flag_seek_value; 443 | 444 | switch (b) 445 | { 446 | case '{': 447 | 448 | if (!new_value (&state, &top, &root, &alloc, json_object)) 449 | goto e_alloc_failure; 450 | 451 | continue; 452 | 453 | case '[': 454 | 455 | if (!new_value (&state, &top, &root, &alloc, json_array)) 456 | goto e_alloc_failure; 457 | 458 | flags |= flag_seek_value; 459 | continue; 460 | 461 | case '"': 462 | 463 | if (!new_value (&state, &top, &root, &alloc, json_string)) 464 | goto e_alloc_failure; 465 | 466 | flags |= flag_string; 467 | 468 | string = top->u.string.ptr; 469 | string_length = 0; 470 | 471 | continue; 472 | 473 | case 't': 474 | 475 | if ((end - i) < 3 || *(++ i) != 'r' || *(++ i) != 'u' || *(++ i) != 'e') 476 | goto e_unknown_value; 477 | 478 | if (!new_value (&state, &top, &root, &alloc, json_boolean)) 479 | goto e_alloc_failure; 480 | 481 | top->u.boolean = 1; 482 | 483 | flags |= flag_next; 484 | break; 485 | 486 | case 'f': 487 | 488 | if ((end - i) < 4 || *(++ i) != 'a' || *(++ i) != 'l' || *(++ i) != 's' || *(++ i) != 'e') 489 | goto e_unknown_value; 490 | 491 | if (!new_value (&state, &top, &root, &alloc, json_boolean)) 492 | goto e_alloc_failure; 493 | 494 | flags |= flag_next; 495 | break; 496 | 497 | case 'n': 498 | 499 | if ((end - i) < 3 || *(++ i) != 'u' || *(++ i) != 'l' || *(++ i) != 'l') 500 | goto e_unknown_value; 501 | 502 | if (!new_value (&state, &top, &root, &alloc, json_null)) 503 | goto e_alloc_failure; 504 | 505 | flags |= flag_next; 506 | break; 507 | 508 | default: 509 | 510 | if (isdigit (b) || b == '-') 511 | { 512 | if (!new_value (&state, &top, &root, &alloc, json_integer)) 513 | goto e_alloc_failure; 514 | 515 | if (!state.first_pass) 516 | { 517 | while (isdigit (b) || b == '+' || b == '-' 518 | || b == 'e' || b == 'E' || b == '.') 519 | { 520 | b = *++ i; 521 | } 522 | 523 | flags |= flag_next | flag_reproc; 524 | break; 525 | } 526 | 527 | flags &= ~ (flag_num_negative | flag_num_e | 528 | flag_num_e_got_sign | flag_num_e_negative | 529 | flag_num_zero); 530 | 531 | num_digits = 0; 532 | num_fraction = 0; 533 | num_e = 0; 534 | 535 | if (b != '-') 536 | { 537 | flags |= flag_reproc; 538 | break; 539 | } 540 | 541 | flags |= flag_num_negative; 542 | continue; 543 | } 544 | else 545 | { 546 | sprintf (error, "%d:%d: Unexpected %c when seeking value", cur_line, e_off, b); 547 | goto e_failed; 548 | } 549 | }; 550 | }; 551 | } 552 | else 553 | { 554 | switch (top->type) 555 | { 556 | case json_object: 557 | 558 | switch (b) 559 | { 560 | whitespace: 561 | continue; 562 | 563 | case '"': 564 | 565 | if (flags & flag_need_comma && (!state.settings.settings & json_relaxed_commas)) 566 | { 567 | sprintf (error, "%d:%d: Expected , before \"", cur_line, e_off); 568 | goto e_failed; 569 | } 570 | 571 | flags |= flag_string; 572 | 573 | string = (json_char *) top->_reserved.object_mem; 574 | string_length = 0; 575 | 576 | break; 577 | 578 | case '}': 579 | 580 | flags = (flags & ~ flag_need_comma) | flag_next; 581 | break; 582 | 583 | case ',': 584 | 585 | if (flags & flag_need_comma) 586 | { 587 | flags &= ~ flag_need_comma; 588 | break; 589 | } 590 | 591 | default: 592 | 593 | sprintf (error, "%d:%d: Unexpected `%c` in object", cur_line, e_off, b); 594 | goto e_failed; 595 | }; 596 | 597 | break; 598 | 599 | case json_integer: 600 | case json_double: 601 | 602 | if (isdigit (b)) 603 | { 604 | ++ num_digits; 605 | 606 | if (top->type == json_integer || flags & flag_num_e) 607 | { 608 | if (! (flags & flag_num_e)) 609 | { 610 | if (flags & flag_num_zero) 611 | { 612 | sprintf (error, "%d:%d: Unexpected `0` before `%c`", cur_line, e_off, b); 613 | goto e_failed; 614 | } 615 | 616 | if (num_digits == 1 && b == '0') 617 | flags |= flag_num_zero; 618 | } 619 | else 620 | { 621 | flags |= flag_num_e_got_sign; 622 | num_e = (num_e * 10) + (b - '0'); 623 | continue; 624 | } 625 | 626 | top->u.integer = (top->u.integer * 10) + (b - '0'); 627 | continue; 628 | } 629 | 630 | num_fraction = (num_fraction * 10) + (b - '0'); 631 | continue; 632 | } 633 | 634 | if (b == '+' || b == '-') 635 | { 636 | if ( (flags & flag_num_e) && !(flags & flag_num_e_got_sign)) 637 | { 638 | flags |= flag_num_e_got_sign; 639 | 640 | if (b == '-') 641 | flags |= flag_num_e_negative; 642 | 643 | continue; 644 | } 645 | } 646 | else if (b == '.' && top->type == json_integer) 647 | { 648 | if (!num_digits) 649 | { 650 | sprintf (error, "%d:%d: Expected digit before `.`", cur_line, e_off); 651 | goto e_failed; 652 | } 653 | 654 | top->type = json_double; 655 | top->u.dbl = (double) top->u.integer; 656 | 657 | num_digits = 0; 658 | continue; 659 | } 660 | 661 | if (! (flags & flag_num_e)) 662 | { 663 | if (top->type == json_double) 664 | { 665 | if (!num_digits) 666 | { 667 | sprintf (error, "%d:%d: Expected digit after `.`", cur_line, e_off); 668 | goto e_failed; 669 | } 670 | 671 | top->u.dbl += ((double) num_fraction) / (pow (10, (double) num_digits)); 672 | } 673 | 674 | if (b == 'e' || b == 'E') 675 | { 676 | flags |= flag_num_e; 677 | 678 | if (top->type == json_integer) 679 | { 680 | top->type = json_double; 681 | top->u.dbl = (double) top->u.integer; 682 | } 683 | 684 | num_digits = 0; 685 | flags &= ~ flag_num_zero; 686 | 687 | continue; 688 | } 689 | } 690 | else 691 | { 692 | if (!num_digits) 693 | { 694 | sprintf (error, "%d:%d: Expected digit after `e`", cur_line, e_off); 695 | goto e_failed; 696 | } 697 | 698 | top->u.dbl *= pow (10, (double) (flags & flag_num_e_negative ? - num_e : num_e)); 699 | } 700 | 701 | if (flags & flag_num_negative) 702 | { 703 | if (top->type == json_integer) 704 | top->u.integer = - top->u.integer; 705 | else 706 | top->u.dbl = - top->u.dbl; 707 | } 708 | 709 | flags |= flag_next | flag_reproc; 710 | break; 711 | 712 | default: 713 | break; 714 | }; 715 | } 716 | 717 | if (flags & flag_reproc) 718 | { 719 | flags &= ~ flag_reproc; 720 | -- i; 721 | } 722 | 723 | if (flags & flag_next) 724 | { 725 | flags = (flags & ~ flag_next) | flag_need_comma; 726 | 727 | if (!top->parent) 728 | { 729 | /* root value done */ 730 | 731 | flags |= flag_done; 732 | continue; 733 | } 734 | 735 | if (top->parent->type == json_array) 736 | flags |= flag_seek_value; 737 | 738 | if (!state.first_pass) 739 | { 740 | json_value * parent = top->parent; 741 | 742 | switch (parent->type) 743 | { 744 | case json_object: 745 | 746 | parent->u.object.values 747 | [parent->u.object.length].value = top; 748 | 749 | break; 750 | 751 | case json_array: 752 | 753 | parent->u.array.values 754 | [parent->u.array.length] = top; 755 | 756 | break; 757 | 758 | default: 759 | break; 760 | }; 761 | } 762 | 763 | if ( (++ top->parent->u.array.length) > state.uint_max) 764 | goto e_overflow; 765 | 766 | top = top->parent; 767 | 768 | continue; 769 | } 770 | } 771 | 772 | alloc = root; 773 | } 774 | 775 | return root; 776 | 777 | e_unknown_value: 778 | 779 | sprintf (error, "%d:%d: Unknown value", cur_line, e_off); 780 | goto e_failed; 781 | 782 | e_alloc_failure: 783 | 784 | strcpy (error, "Memory allocation failure"); 785 | goto e_failed; 786 | 787 | e_overflow: 788 | 789 | sprintf (error, "%d:%d: Too long (caught overflow)", cur_line, e_off); 790 | goto e_failed; 791 | 792 | e_failed: 793 | 794 | if (error_buf) 795 | { 796 | if (*error) 797 | strcpy (error_buf, error); 798 | else 799 | strcpy (error_buf, "Unknown error"); 800 | } 801 | 802 | if (state.first_pass) 803 | alloc = root; 804 | 805 | while (alloc) 806 | { 807 | top = alloc->_reserved.next_alloc; 808 | state.settings.mem_free (alloc, state.settings.user_data); 809 | alloc = top; 810 | } 811 | 812 | if (!state.first_pass) 813 | json_value_free_ex (&state.settings, root); 814 | 815 | return 0; 816 | } 817 | 818 | json_value * json_parse (const json_char * json, size_t length) 819 | { 820 | json_settings settings = { 0 }; 821 | return json_parse_ex (&settings, json, length, 0); 822 | } 823 | 824 | void json_value_free_ex (json_settings * settings, json_value * value) 825 | { 826 | json_value * cur_value; 827 | 828 | if (!value) 829 | return; 830 | 831 | value->parent = 0; 832 | 833 | while (value) 834 | { 835 | switch (value->type) 836 | { 837 | case json_array: 838 | 839 | if (!value->u.array.length) 840 | { 841 | settings->mem_free (value->u.array.values, settings->user_data); 842 | break; 843 | } 844 | 845 | value = value->u.array.values [-- value->u.array.length]; 846 | continue; 847 | 848 | case json_object: 849 | 850 | if (!value->u.object.length) 851 | { 852 | settings->mem_free (value->u.object.values, settings->user_data); 853 | break; 854 | } 855 | 856 | value = value->u.object.values [-- value->u.object.length].value; 857 | continue; 858 | 859 | case json_string: 860 | 861 | settings->mem_free (value->u.string.ptr, settings->user_data); 862 | break; 863 | 864 | default: 865 | break; 866 | }; 867 | 868 | cur_value = value; 869 | value = value->parent; 870 | settings->mem_free (cur_value, settings->user_data); 871 | } 872 | } 873 | 874 | void json_value_free (json_value * value) 875 | { 876 | json_settings settings = { 0 }; 877 | settings.mem_free = default_free; 878 | json_value_free_ex (&settings, value); 879 | } 880 | 881 | -------------------------------------------------------------------------------- /src/oauth.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief OAuth.net implementation in POSIX-C. 3 | * @file oauth.h 4 | * @author Robin Gareus 5 | * 6 | * Copyright 2007-2012 Robin Gareus 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | * 26 | */ 27 | #ifndef _OAUTH_H 28 | #define _OAUTH_H 1 29 | 30 | #ifndef DOXYGEN_IGNORE 31 | // liboauth version 32 | #define LIBOAUTH_VERSION "1.0.1" 33 | #define LIBOAUTH_VERSION_MAJOR 1 34 | #define LIBOAUTH_VERSION_MINOR 0 35 | #define LIBOAUTH_VERSION_MICRO 1 36 | 37 | //interface revision number 38 | //http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html 39 | #define LIBOAUTH_CUR 8 40 | #define LIBOAUTH_REV 5 41 | #define LIBOAUTH_AGE 8 42 | 43 | #ifdef __GNUC__ 44 | # define OA_GCC_VERSION_AT_LEAST(x,y) (__GNUC__ > x || __GNUC__ == x && __GNUC_MINOR__ >= y) 45 | #else 46 | # define OA_GCC_VERSION_AT_LEAST(x,y) 0 47 | #endif 48 | 49 | #ifndef attribute_deprecated 50 | #if OA_GCC_VERSION_AT_LEAST(3,1) 51 | # define attribute_deprecated __attribute__((deprecated)) 52 | #else 53 | # define attribute_deprecated 54 | #endif 55 | #endif 56 | 57 | #endif /* doxygen ignore */ 58 | 59 | #ifdef __cplusplus 60 | extern "C" { 61 | #endif 62 | 63 | /** \enum OAuthMethod 64 | * signature method to used for signing the request. 65 | */ 66 | typedef enum { 67 | OA_HMAC=0, ///< use HMAC-SHA1 request signing method 68 | OA_RSA, ///< use RSA signature 69 | OA_PLAINTEXT ///< use plain text signature (for testing only) 70 | } OAuthMethod; 71 | 72 | /** 73 | * Base64 encode and return size data in 'src'. The caller must free the 74 | * returned string. 75 | * 76 | * @param size The size of the data in src 77 | * @param src The data to be base64 encode 78 | * @return encoded string otherwise NULL 79 | */ 80 | char *oauth_encode_base64(int size, const unsigned char *src); 81 | 82 | /** 83 | * Decode the base64 encoded string 'src' into the memory pointed to by 84 | * 'dest'. 85 | * 86 | * @param dest Pointer to memory for holding the decoded string. 87 | * Must be large enough to receive the decoded string. 88 | * @param src A base64 encoded string. 89 | * @return the length of the decoded string if decode 90 | * succeeded otherwise 0. 91 | */ 92 | int oauth_decode_base64(unsigned char *dest, const char *src); 93 | 94 | /** 95 | * Escape 'string' according to RFC3986 and 96 | * http://oauth.net/core/1.0/#encoding_parameters. 97 | * 98 | * @param string The data to be encoded 99 | * @return encoded string otherwise NULL 100 | * The caller must free the returned string. 101 | */ 102 | char *oauth_url_escape(const char *string); 103 | 104 | /** 105 | * Parse RFC3986 encoded 'string' back to unescaped version. 106 | * 107 | * @param string The data to be unescaped 108 | * @param olen unless NULL the length of the returned string is stored there. 109 | * @return decoded string or NULL 110 | * The caller must free the returned string. 111 | */ 112 | char *oauth_url_unescape(const char *string, size_t *olen); 113 | 114 | 115 | /** 116 | * returns base64 encoded HMAC-SHA1 signature for 117 | * given message and key. 118 | * both data and key need to be urlencoded. 119 | * 120 | * the returned string needs to be freed by the caller 121 | * 122 | * @param m message to be signed 123 | * @param k key used for signing 124 | * @return signature string. 125 | */ 126 | char *oauth_sign_hmac_sha1 (const char *m, const char *k); 127 | 128 | /** 129 | * same as \ref oauth_sign_hmac_sha1 but allows 130 | * to specify length of message and key (in case they contain null chars). 131 | * 132 | * @param m message to be signed 133 | * @param ml length of message 134 | * @param k key used for signing 135 | * @param kl length of key 136 | * @return signature string. 137 | */ 138 | char *oauth_sign_hmac_sha1_raw (const char *m, const size_t ml, const char *k, const size_t kl); 139 | 140 | /** 141 | * returns plaintext signature for the given key. 142 | * 143 | * the returned string needs to be freed by the caller 144 | * 145 | * @param m message to be signed 146 | * @param k key used for signing 147 | * @return signature string 148 | */ 149 | char *oauth_sign_plaintext (const char *m, const char *k); 150 | 151 | /** 152 | * returns RSA-SHA1 signature for given data. 153 | * the returned signature needs to be freed by the caller. 154 | * 155 | * @param m message to be signed 156 | * @param k private-key PKCS and Base64-encoded 157 | * @return base64 encoded signature string. 158 | */ 159 | char *oauth_sign_rsa_sha1 (const char *m, const char *k); 160 | 161 | /** 162 | * verify RSA-SHA1 signature. 163 | * 164 | * returns the output of EVP_VerifyFinal() for a given message, 165 | * cert/pubkey and signature. 166 | * 167 | * @param m message to be verified 168 | * @param c public-key or x509 certificate 169 | * @param s base64 encoded signature 170 | * @return 1 for a correct signature, 0 for failure and -1 if some other error occurred 171 | */ 172 | int oauth_verify_rsa_sha1 (const char *m, const char *c, const char *s); 173 | 174 | /** 175 | * url-escape strings and concatenate with '&' separator. 176 | * The number of strings to be concatenated must be 177 | * given as first argument. 178 | * all arguments thereafter must be of type (char *) 179 | * 180 | * @param len the number of arguments to follow this parameter 181 | * 182 | * @return pointer to memory holding the concatenated 183 | * strings - needs to be free(d) by the caller. or NULL 184 | * in case we ran out of memory. 185 | */ 186 | char *oauth_catenc(int len, ...); 187 | 188 | /** 189 | * splits the given url into a parameter array. 190 | * (see \ref oauth_serialize_url and \ref oauth_serialize_url_parameters for the reverse) 191 | * (see \ref oauth_split_post_paramters for a more generic version) 192 | * 193 | * @param url the url or query-string to parse; may be NULL 194 | * @param argv pointer to a (char *) array where the results are stored. 195 | * The array is re-allocated to match the number of parameters and each 196 | * parameter-string is allocated with strdup. - The memory needs to be freed 197 | * by the caller. 198 | * 199 | * @return number of parameter(s) in array. 200 | */ 201 | int oauth_split_url_parameters(const char *url, char ***argv); 202 | 203 | /** 204 | * splits the given url into a parameter array. 205 | * (see \ref oauth_serialize_url and \ref oauth_serialize_url_parameters for the reverse) 206 | * 207 | * @param url the url or query-string to parse. 208 | * @param argv pointer to a (char *) array where the results are stored. 209 | * The array is re-allocated to match the number of parameters and each 210 | * parameter-string is allocated with strdup. - The memory needs to be freed 211 | * by the caller. 212 | * @param qesc use query parameter escape (vs post-param-escape) - if set 213 | * to 1 all '+' are treated as spaces ' ' 214 | * 215 | * @return number of parameter(s) in array. 216 | */ 217 | int oauth_split_post_paramters(const char *url, char ***argv, short qesc); 218 | 219 | /** 220 | * build a url query string from an array. 221 | * 222 | * @param argc the total number of elements in the array 223 | * @param start element in the array at which to start concatenating. 224 | * @param argv parameter-array to concatenate. 225 | * @return url string needs to be freed by the caller. 226 | * 227 | */ 228 | char *oauth_serialize_url (int argc, int start, char **argv); 229 | 230 | /** 231 | * encode query parameters from an array. 232 | * 233 | * @param argc the total number of elements in the array 234 | * @param start element in the array at which to start concatenating. 235 | * @param argv parameter-array to concatenate. 236 | * @param sep separator for parameters (usually "&") 237 | * @param mod - bitwise modifiers: 238 | * 1: skip all values that start with "oauth_" 239 | * 2: skip all values that don't start with "oauth_" 240 | * 4: double quotation marks are added around values (use with sep ", " for HTTP Authorization header). 241 | * @return url string needs to be freed by the caller. 242 | */ 243 | char *oauth_serialize_url_sep (int argc, int start, char **argv, char *sep, int mod); 244 | 245 | /** 246 | * build a query parameter string from an array. 247 | * 248 | * This function is a shortcut for \ref oauth_serialize_url (argc, 1, argv). 249 | * It strips the leading host/path, which is usually the first 250 | * element when using oauth_split_url_parameters on an URL. 251 | * 252 | * @param argc the total number of elements in the array 253 | * @param argv parameter-array to concatenate. 254 | * @return url string needs to be freed by the caller. 255 | */ 256 | char *oauth_serialize_url_parameters (int argc, char **argv); 257 | 258 | /** 259 | * generate a random string between 15 and 32 chars length 260 | * and return a pointer to it. The value needs to be freed by the 261 | * caller 262 | * 263 | * @return zero terminated random string. 264 | */ 265 | char *oauth_gen_nonce(); 266 | 267 | /** 268 | * string compare function for oauth parameters. 269 | * 270 | * used with qsort. needed to normalize request parameters. 271 | * see http://oauth.net/core/1.0/#anchor14 272 | */ 273 | int oauth_cmpstringp(const void *p1, const void *p2); 274 | 275 | 276 | /** 277 | * search array for parameter key. 278 | * @param argv length of array to search 279 | * @param argc parameter array to search 280 | * @param key key of parameter to check. 281 | * 282 | * @return FALSE (0) if array does not contain a parameter with given key, TRUE (1) otherwise. 283 | */ 284 | int oauth_param_exists(char **argv, int argc, char *key); 285 | 286 | /** 287 | * add query parameter to array 288 | * 289 | * @param argcp pointer to array length int 290 | * @param argvp pointer to array values 291 | * @param addparam parameter to add (eg. "foo=bar") 292 | */ 293 | void oauth_add_param_to_array(int *argcp, char ***argvp, const char *addparam); 294 | 295 | /** 296 | * free array args 297 | * 298 | * @param argcp pointer to array length int 299 | * @param argvp pointer to array values to be free()d 300 | */ 301 | void oauth_free_array(int *argcp, char ***argvp); 302 | 303 | /** 304 | * compare two strings in constant-time (as to not let an 305 | * attacker guess how many leading chars are correct: 306 | * http://rdist.root.org/2010/01/07/timing-independent-array-comparison/ ) 307 | * 308 | * @param a string to compare 309 | * @param b string to compare 310 | * @param len_a length of string a 311 | * @param len_b length of string b 312 | * 313 | * returns 0 (false) if strings are not equal, and 1 (true) if strings are equal. 314 | */ 315 | int oauth_time_independent_equals_n(const char* a, const char* b, size_t len_a, size_t len_b); 316 | 317 | /** 318 | * @deprecated Use oauth_time_independent_equals_n() instead. 319 | */ 320 | int oauth_time_indepenent_equals_n(const char* a, const char* b, size_t len_a, size_t len_b) attribute_deprecated; 321 | 322 | /** 323 | * compare two strings in constant-time. 324 | * wrapper to \ref oauth_time_independent_equals_n 325 | * which calls strlen() for each argument. 326 | * 327 | * @param a string to compare 328 | * @param b string to compare 329 | * 330 | * returns 0 (false) if strings are not equal, and 1 (true) if strings are equal. 331 | */ 332 | int oauth_time_independent_equals(const char* a, const char* b); 333 | 334 | /** 335 | * @deprecated Use oauth_time_independent_equals() instead. 336 | */ 337 | int oauth_time_indepenent_equals(const char* a, const char* b) attribute_deprecated; 338 | 339 | /** 340 | * calculate OAuth-signature for a given HTTP request URL, parameters and oauth-tokens. 341 | * 342 | * if 'postargs' is NULL a "GET" request is signed and the 343 | * signed URL is returned. Else this fn will modify 'postargs' 344 | * to point to memory that contains the signed POST-variables 345 | * and returns the base URL. 346 | * 347 | * both, the return value and (if given) 'postargs' need to be freed 348 | * by the caller. 349 | * 350 | * @param url The request URL to be signed. append all GET or POST 351 | * query-parameters separated by either '?' or '&' to this parameter. 352 | * 353 | * @param postargs This parameter points to an area where the return value 354 | * is stored. If 'postargs' is NULL, no value is stored. 355 | * 356 | * @param method specify the signature method to use. It is of type 357 | * \ref OAuthMethod and most likely \ref OA_HMAC. 358 | * 359 | * @param http_method The HTTP request method to use (ie "GET", "PUT",..) 360 | * If NULL is given as 'http_method' this defaults to "GET" when 361 | * 'postargs' is also NULL and when postargs is not NULL "POST" is used. 362 | * 363 | * @param c_key consumer key 364 | * @param c_secret consumer secret 365 | * @param t_key token key 366 | * @param t_secret token secret 367 | * 368 | * @return the signed url or NULL if an error occurred. 369 | * 370 | */ 371 | char *oauth_sign_url2 (const char *url, char **postargs, 372 | OAuthMethod method, 373 | const char *http_method, //< HTTP request method 374 | const char *c_key, //< consumer key - posted plain text 375 | const char *c_secret, //< consumer secret - used as 1st part of secret-key 376 | const char *t_key, //< token key - posted plain text in URL 377 | const char *t_secret //< token secret - used as 2st part of secret-key 378 | ); 379 | 380 | /** 381 | * @deprecated Use oauth_sign_url2() instead. 382 | */ 383 | char *oauth_sign_url (const char *url, char **postargs, 384 | OAuthMethod method, 385 | const char *c_key, //< consumer key - posted plain text 386 | const char *c_secret, //< consumer secret - used as 1st part of secret-key 387 | const char *t_key, //< token key - posted plain text in URL 388 | const char *t_secret //< token secret - used as 2st part of secret-key 389 | ) attribute_deprecated; 390 | 391 | 392 | /** 393 | * the back-end behind by /ref oauth_sign_array2. 394 | * however it does not serialize the signed URL again. 395 | * The user needs to call /ref oauth_serialize_url (oA) 396 | * and /ref oauth_free_array to do so. 397 | * 398 | * This allows to split parts of the URL to be used for 399 | * OAuth HTTP Authorization header: 400 | * see http://oauth.net/core/1.0a/#consumer_req_param 401 | * the oauthtest2 example code does so. 402 | * 403 | * 404 | * @param argcp pointer to array length int 405 | * @param argvp pointer to array values 406 | * (argv[0]="http://example.org:80/" argv[1]="first=QueryParamater" .. 407 | * the array is modified: fi. oauth_ parameters are added) 408 | * These arrays can be generated with /ref oauth_split_url_parameters 409 | * or /ref oauth_split_post_paramters. 410 | * 411 | * @param postargs This parameter points to an area where the return value 412 | * is stored. If 'postargs' is NULL, no value is stored. 413 | * 414 | * @param method specify the signature method to use. It is of type 415 | * \ref OAuthMethod and most likely \ref OA_HMAC. 416 | * 417 | * @param http_method The HTTP request method to use (ie "GET", "PUT",..) 418 | * If NULL is given as 'http_method' this defaults to "GET" when 419 | * 'postargs' is also NULL and when postargs is not NULL "POST" is used. 420 | * 421 | * @param c_key consumer key 422 | * @param c_secret consumer secret 423 | * @param t_key token key 424 | * @param t_secret token secret 425 | * 426 | * @return void 427 | * 428 | */ 429 | void oauth_sign_array2_process (int *argcp, char***argvp, 430 | char **postargs, 431 | OAuthMethod method, 432 | const char *http_method, //< HTTP request method 433 | const char *c_key, //< consumer key - posted plain text 434 | const char *c_secret, //< consumer secret - used as 1st part of secret-key 435 | const char *t_key, //< token key - posted plain text in URL 436 | const char *t_secret //< token secret - used as 2st part of secret-key 437 | ); 438 | 439 | /** 440 | * same as /ref oauth_sign_url 441 | * with the url already split into parameter array 442 | * 443 | * @param argcp pointer to array length int 444 | * @param argvp pointer to array values 445 | * (argv[0]="http://example.org:80/" argv[1]="first=QueryParamater" .. 446 | * the array is modified: fi. oauth_ parameters are added) 447 | * These arrays can be generated with /ref oauth_split_url_parameters 448 | * or /ref oauth_split_post_paramters. 449 | * 450 | * @param postargs This parameter points to an area where the return value 451 | * is stored. If 'postargs' is NULL, no value is stored. 452 | * 453 | * @param method specify the signature method to use. It is of type 454 | * \ref OAuthMethod and most likely \ref OA_HMAC. 455 | * 456 | * @param http_method The HTTP request method to use (ie "GET", "PUT",..) 457 | * If NULL is given as 'http_method' this defaults to "GET" when 458 | * 'postargs' is also NULL and when postargs is not NULL "POST" is used. 459 | * 460 | * @param c_key consumer key 461 | * @param c_secret consumer secret 462 | * @param t_key token key 463 | * @param t_secret token secret 464 | * 465 | * @return the signed url or NULL if an error occurred. 466 | */ 467 | char *oauth_sign_array2 (int *argcp, char***argvp, 468 | char **postargs, 469 | OAuthMethod method, 470 | const char *http_method, //< HTTP request method 471 | const char *c_key, //< consumer key - posted plain text 472 | const char *c_secret, //< consumer secret - used as 1st part of secret-key 473 | const char *t_key, //< token key - posted plain text in URL 474 | const char *t_secret //< token secret - used as 2st part of secret-key 475 | ); 476 | 477 | /** 478 | * @deprecated Use oauth_sign_array2() instead. 479 | */ 480 | char *oauth_sign_array (int *argcp, char***argvp, 481 | char **postargs, 482 | OAuthMethod method, 483 | const char *c_key, //< consumer key - posted plain text 484 | const char *c_secret, //< consumer secret - used as 1st part of secret-key 485 | const char *t_key, //< token key - posted plain text in URL 486 | const char *t_secret //< token secret - used as 2st part of secret-key 487 | ) attribute_deprecated; 488 | 489 | 490 | /** 491 | * calculate body hash (sha1sum) of given file and return 492 | * a oauth_body_hash=xxxx parameter to be added to the request. 493 | * The returned string needs to be freed by the calling function. 494 | * 495 | * see 496 | * http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html 497 | * 498 | * @param filename the filename to calculate the hash for 499 | * 500 | * @return URL oauth_body_hash parameter string 501 | */ 502 | char *oauth_body_hash_file(char *filename); 503 | 504 | /** 505 | * calculate body hash (sha1sum) of given data and return 506 | * a oauth_body_hash=xxxx parameter to be added to the request. 507 | * The returned string needs to be freed by the calling function. 508 | * The returned string is not yet url-escaped and suitable to be 509 | * passed as argument to \ref oauth_catenc. 510 | * 511 | * see 512 | * http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html 513 | * 514 | * @param length length of the data parameter in bytes 515 | * @param data to calculate the hash for 516 | * 517 | * @return URL oauth_body_hash parameter string 518 | */ 519 | char *oauth_body_hash_data(size_t length, const char *data); 520 | 521 | /** 522 | * base64 encode digest, free it and return a URL parameter 523 | * with the oauth_body_hash. The returned hash needs to be freed by the 524 | * calling function. The returned string is not yet url-escaped and 525 | * thus suitable to be passed to \ref oauth_catenc. 526 | * 527 | * @param len length of the digest to encode 528 | * @param digest hash value to encode 529 | * 530 | * @return URL oauth_body_hash parameter string 531 | */ 532 | char *oauth_body_hash_encode(size_t len, unsigned char *digest); 533 | 534 | /** 535 | * xep-0235 - TODO 536 | */ 537 | char *oauth_sign_xmpp (const char *xml, 538 | OAuthMethod method, 539 | const char *c_secret, //< consumer secret - used as 1st part of secret-key 540 | const char *t_secret //< token secret - used as 2st part of secret-key 541 | ); 542 | 543 | /** 544 | * do a HTTP GET request, wait for it to finish 545 | * and return the content of the reply. 546 | * (requires libcurl or a command-line HTTP client) 547 | * 548 | * If compiled without libcurl this function calls 549 | * a command-line executable defined in the environment variable 550 | * OAUTH_HTTP_GET_CMD - it defaults to 551 | * curl -sA 'liboauth-agent/0.1' '%%u' 552 | * where %%u is replaced with the URL and query parameters. 553 | * 554 | * bash & wget example: 555 | * export OAUTH_HTTP_CMD="wget -q -U 'liboauth-agent/0.1' '%u' " 556 | * 557 | * WARNING: this is a tentative function. it's convenient and handy for testing 558 | * or developing OAuth code. But don't rely on this function 559 | * to become a stable part of this API. It does not do 560 | * much error checking or handling for one thing.. 561 | * 562 | * NOTE: \a u and \a q are just concatenated with a '?' in between, 563 | * unless \a q is NULL. in which case only \a u will be used. 564 | * 565 | * @param u base url to get 566 | * @param q query string to send along with the HTTP request or NULL. 567 | * @return In case of an error NULL is returned; otherwise a pointer to the 568 | * replied content from HTTP server. latter needs to be freed by caller. 569 | * 570 | * @deprecated use libcurl - http://curl.haxx.se/libcurl/c/ 571 | */ 572 | char *oauth_http_get (const char *u, const char *q) attribute_deprecated; 573 | 574 | /** 575 | * do a HTTP GET request, wait for it to finish 576 | * and return the content of the reply. 577 | * 578 | * (requires libcurl) 579 | * 580 | * This is equivalent to /ref oauth_http_get but allows to 581 | * specifiy a custom HTTP header and has 582 | * has no support for commandline-curl. 583 | * 584 | * If liboauth is compiled without libcurl this function 585 | * always returns NULL. 586 | * 587 | * @param u base url to get 588 | * @param q query string to send along with the HTTP request or NULL. 589 | * @param customheader specify custom HTTP header (or NULL for none) 590 | * Multiple header elements can be passed separating them with "\r\n" 591 | * @return In case of an error NULL is returned; otherwise a pointer to the 592 | * replied content from HTTP server. latter needs to be freed by caller. 593 | * 594 | * @deprecated use libcurl - http://curl.haxx.se/libcurl/c/ 595 | */ 596 | char *oauth_http_get2 (const char *u, const char *q, const char *customheader) attribute_deprecated; 597 | 598 | 599 | /** 600 | * do a HTTP POST request, wait for it to finish 601 | * and return the content of the reply. 602 | * (requires libcurl or a command-line HTTP client) 603 | * 604 | * If compiled without libcurl this function calls 605 | * a command-line executable defined in the environment variable 606 | * OAUTH_HTTP_CMD - it defaults to 607 | * curl -sA 'liboauth-agent/0.1' -d '%%p' '%%u' 608 | * where %%p is replaced with the postargs and %%u is replaced with 609 | * the URL. 610 | * 611 | * bash & wget example: 612 | * export OAUTH_HTTP_CMD="wget -q -U 'liboauth-agent/0.1' --post-data='%p' '%u' " 613 | * 614 | * NOTE: This function uses the curl's default HTTP-POST Content-Type: 615 | * application/x-www-form-urlencoded which is the only option allowed 616 | * by oauth core 1.0 spec. Experimental code can use the Environment variable 617 | * to transmit custom HTTP headers or parameters. 618 | * 619 | * WARNING: this is a tentative function. it's convenient and handy for testing 620 | * or developing OAuth code. But don't rely on this function 621 | * to become a stable part of this API. It does not do 622 | * much error checking for one thing.. 623 | * 624 | * @param u url to query 625 | * @param p postargs to send along with the HTTP request. 626 | * @return replied content from HTTP server. needs to be freed by caller. 627 | * 628 | * @deprecated use libcurl - http://curl.haxx.se/libcurl/c/ 629 | */ 630 | char *oauth_http_post (const char *u, const char *p) attribute_deprecated; 631 | 632 | /** 633 | * do a HTTP POST request, wait for it to finish 634 | * and return the content of the reply. 635 | * (requires libcurl) 636 | * 637 | * It's equivalent to /ref oauth_http_post, but offers 638 | * the possibility to specify a custom HTTP header and 639 | * has no support for commandline-curl. 640 | * 641 | * If liboauth is compiled without libcurl this function 642 | * always returns NULL. 643 | * 644 | * @param u url to query 645 | * @param p postargs to send along with the HTTP request. 646 | * @param customheader specify custom HTTP header (or NULL for none) 647 | * Multiple header elements can be passed separating them with "\r\n" 648 | * @return replied content from HTTP server. needs to be freed by caller. 649 | * 650 | * @deprecated use libcurl - http://curl.haxx.se/libcurl/c/ 651 | */ 652 | char *oauth_http_post2 (const char *u, const char *p, const char *customheader) attribute_deprecated; 653 | 654 | 655 | /** 656 | * http post raw data from file. 657 | * the returned string needs to be freed by the caller 658 | * (requires libcurl) 659 | * 660 | * see dislaimer: /ref oauth_http_post 661 | * 662 | * @param u url to retrieve 663 | * @param fn filename of the file to post along 664 | * @param len length of the file in bytes. set to '0' for autodetection 665 | * @param customheader specify custom HTTP header (or NULL for default). 666 | * Multiple header elements can be passed separating them with "\r\n" 667 | * @return returned HTTP reply or NULL on error 668 | * 669 | * @deprecated use libcurl - http://curl.haxx.se/libcurl/c/ 670 | */ 671 | char *oauth_post_file (const char *u, const char *fn, const size_t len, const char *customheader) attribute_deprecated; 672 | 673 | /** 674 | * http post raw data 675 | * the returned string needs to be freed by the caller 676 | * (requires libcurl) 677 | * 678 | * see dislaimer: /ref oauth_http_post 679 | * 680 | * @param u url to retrieve 681 | * @param data data to post 682 | * @param len length of the data in bytes. 683 | * @param customheader specify custom HTTP header (or NULL for default) 684 | * Multiple header elements can be passed separating them with "\r\n" 685 | * @return returned HTTP reply or NULL on error 686 | * 687 | * @deprecated use libcurl - http://curl.haxx.se/libcurl/c/ 688 | */ 689 | char *oauth_post_data (const char *u, const char *data, size_t len, const char *customheader) attribute_deprecated; 690 | 691 | /** 692 | * http post raw data, with callback. 693 | * the returned string needs to be freed by the caller 694 | * (requires libcurl) 695 | * 696 | * Invokes the callback - in no particular order - when HTTP-request status updates occur. 697 | * The callback is called with: 698 | * void * callback_data: supplied on function call. 699 | * int type: 0=data received, 1=data sent. 700 | * size_t size: amount of data received or amount of data sent so far 701 | * size_t totalsize: original amount of data to send, or amount of data received 702 | * 703 | * @param u url to retrieve 704 | * @param data data to post along 705 | * @param len length of the file in bytes. set to '0' for autodetection 706 | * @param customheader specify custom HTTP header (or NULL for default) 707 | * Multiple header elements can be passed separating them with "\r\n" 708 | * @param callback specify the callback function 709 | * @param callback_data specify data to pass to the callback function 710 | * @return returned HTTP reply or NULL on error 711 | * 712 | * @deprecated use libcurl - http://curl.haxx.se/libcurl/c/ 713 | */ 714 | char *oauth_post_data_with_callback (const char *u, 715 | const char *data, 716 | size_t len, 717 | const char *customheader, 718 | void (*callback)(void*,int,size_t,size_t), 719 | void *callback_data) attribute_deprecated; 720 | 721 | /** 722 | * http send raw data. similar to /ref oauth_http_post but provides 723 | * for specifying the HTTP request method. 724 | * 725 | * the returned string needs to be freed by the caller 726 | * (requires libcurl) 727 | * 728 | * see dislaimer: /ref oauth_http_post 729 | * 730 | * @param u url to retrieve 731 | * @param data data to post 732 | * @param len length of the data in bytes. 733 | * @param customheader specify custom HTTP header (or NULL for default) 734 | * Multiple header elements can be passed separating them with "\r\n" 735 | * @param httpMethod specify http verb ("GET"/"POST"/"PUT"/"DELETE") to be used. if httpMethod is NULL, a POST is executed. 736 | * @return returned HTTP reply or NULL on error 737 | * 738 | * @deprecated use libcurl - http://curl.haxx.se/libcurl/c/ 739 | */ 740 | char *oauth_send_data (const char *u, 741 | const char *data, 742 | size_t len, 743 | const char *customheader, 744 | const char *httpMethod) attribute_deprecated; 745 | 746 | /** 747 | * http post raw data, with callback. 748 | * the returned string needs to be freed by the caller 749 | * (requires libcurl) 750 | * 751 | * Invokes the callback - in no particular order - when HTTP-request status updates occur. 752 | * The callback is called with: 753 | * void * callback_data: supplied on function call. 754 | * int type: 0=data received, 1=data sent. 755 | * size_t size: amount of data received or amount of data sent so far 756 | * size_t totalsize: original amount of data to send, or amount of data received 757 | * 758 | * @param u url to retrieve 759 | * @param data data to post along 760 | * @param len length of the file in bytes. set to '0' for autodetection 761 | * @param customheader specify custom HTTP header (or NULL for default) 762 | * Multiple header elements can be passed separating them with "\r\n" 763 | * @param callback specify the callback function 764 | * @param callback_data specify data to pass to the callback function 765 | * @param httpMethod specify http verb ("GET"/"POST"/"PUT"/"DELETE") to be used. 766 | * @return returned HTTP reply or NULL on error 767 | * 768 | * @deprecated use libcurl - http://curl.haxx.se/libcurl/c/ 769 | */ 770 | char *oauth_send_data_with_callback (const char *u, 771 | const char *data, 772 | size_t len, 773 | const char *customheader, 774 | void (*callback)(void*,int,size_t,size_t), 775 | void *callback_data, 776 | const char *httpMethod) attribute_deprecated; 777 | 778 | #ifdef __cplusplus 779 | } /* extern "C" */ 780 | #endif /* __cplusplus */ 781 | 782 | #endif 783 | /* vi:set ts=8 sts=2 sw=2: */ 784 | -------------------------------------------------------------------------------- /src/oauth.c: -------------------------------------------------------------------------------- 1 | /* 2 | * OAuth string functions in POSIX-C. 3 | * 4 | * Copyright 2007-2013 Robin Gareus 5 | * 6 | * The base64 functions are by Jan-Henrik Haukeland, 7 | * and un/escape_url() was inspired by libcurl's curl_escape under ISC-license 8 | * many thanks to Daniel Stenberg . 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in 18 | * all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | * THE SOFTWARE. 27 | * 28 | */ 29 | //#if HAVE_CONFIG_H 30 | # include "config.h" 31 | //#endif 32 | 33 | #define WIPE_MEMORY ///< overwrite sensitve data before free()ing it. 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include // isxdigit 42 | 43 | #include "xmalloc.h" 44 | #include "oauth.h" 45 | 46 | #ifndef WIN32 // getpid() on POSIX systems 47 | #include 48 | #include 49 | #else 50 | #define snprintf _snprintf 51 | #define strncasecmp strnicmp 52 | #endif 53 | 54 | /** 55 | * Base64 encode one byte 56 | */ 57 | char oauth_b64_encode(unsigned char u) { 58 | if(u < 26) return 'A'+u; 59 | if(u < 52) return 'a'+(u-26); 60 | if(u < 62) return '0'+(u-52); 61 | if(u == 62) return '+'; 62 | return '/'; 63 | } 64 | 65 | /** 66 | * Decode a single base64 character. 67 | */ 68 | unsigned char oauth_b64_decode(char c) { 69 | if(c >= 'A' && c <= 'Z') return(c - 'A'); 70 | if(c >= 'a' && c <= 'z') return(c - 'a' + 26); 71 | if(c >= '0' && c <= '9') return(c - '0' + 52); 72 | if(c == '+') return 62; 73 | return 63; 74 | } 75 | 76 | /** 77 | * Return TRUE if 'c' is a valid base64 character, otherwise FALSE 78 | */ 79 | int oauth_b64_is_base64(char c) { 80 | if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || 81 | (c >= '0' && c <= '9') || (c == '+') || 82 | (c == '/') || (c == '=')) { 83 | return 1; 84 | } 85 | return 0; 86 | } 87 | 88 | /** 89 | * Base64 encode and return size data in 'src'. The caller must free the 90 | * returned string. 91 | * 92 | * @param size The size of the data in src 93 | * @param src The data to be base64 encode 94 | * @return encoded string otherwise NULL 95 | */ 96 | char *oauth_encode_base64(int size, const unsigned char *src) { 97 | int i; 98 | char *out, *p; 99 | 100 | if(!src) return NULL; 101 | if(!size) size= strlen((char *)src); 102 | out= (char*) xcalloc(sizeof(char), size*4/3+4); 103 | p= out; 104 | 105 | for(i=0; i>2; 112 | b5= ((b1&0x3)<<4)|(b2>>4); 113 | b6= ((b2&0xf)<<2)|(b3>>6); 114 | b7= b3&0x3f; 115 | 116 | *p++= oauth_b64_encode(b4); 117 | *p++= oauth_b64_encode(b5); 118 | 119 | if(i+1>4) ); 166 | 167 | if(c3 != '=') *p++=(((b2&0xf)<<4)|(b3>>2) ); 168 | if(c4 != '=') *p++=(((b3&0x3)<<6)|b4 ); 169 | } 170 | free(buf); 171 | dest[p-dest]='\0'; 172 | return(p-dest); 173 | } 174 | return 0; 175 | } 176 | 177 | /** 178 | * Escape 'string' according to RFC3986 and 179 | * http://oauth.net/core/1.0/#encoding_parameters. 180 | * 181 | * @param string The data to be encoded 182 | * @return encoded string otherwise NULL 183 | * The caller must free the returned string. 184 | */ 185 | char *oauth_url_escape(const char *string) { 186 | size_t alloc, newlen; 187 | char *ns = NULL, *testing_ptr = NULL; 188 | unsigned char in; 189 | size_t strindex=0; 190 | size_t length; 191 | 192 | if (!string) return xstrdup(""); 193 | 194 | alloc = strlen(string)+1; 195 | newlen = alloc; 196 | 197 | ns = (char*) xmalloc(alloc); 198 | 199 | length = alloc-1; 200 | while(length--) { 201 | in = *string; 202 | 203 | switch(in){ 204 | case '0': case '1': case '2': case '3': case '4': 205 | case '5': case '6': case '7': case '8': case '9': 206 | case 'a': case 'b': case 'c': case 'd': case 'e': 207 | case 'f': case 'g': case 'h': case 'i': case 'j': 208 | case 'k': case 'l': case 'm': case 'n': case 'o': 209 | case 'p': case 'q': case 'r': case 's': case 't': 210 | case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': 211 | case 'A': case 'B': case 'C': case 'D': case 'E': 212 | case 'F': case 'G': case 'H': case 'I': case 'J': 213 | case 'K': case 'L': case 'M': case 'N': case 'O': 214 | case 'P': case 'Q': case 'R': case 'S': case 'T': 215 | case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': 216 | case '_': case '~': case '.': case '-': 217 | ns[strindex++]=in; 218 | break; 219 | default: 220 | newlen += 2; /* this'll become a %XX */ 221 | if(newlen > alloc) { 222 | alloc *= 2; 223 | testing_ptr = (char*) xrealloc(ns, alloc); 224 | ns = testing_ptr; 225 | } 226 | snprintf(&ns[strindex], 4, "%%%02X", in); 227 | strindex+=3; 228 | break; 229 | } 230 | string++; 231 | } 232 | ns[strindex]=0; 233 | return ns; 234 | } 235 | 236 | #ifndef ISXDIGIT 237 | # define ISXDIGIT(x) (isxdigit((int) ((unsigned char)x))) 238 | #endif 239 | 240 | /** 241 | * Parse RFC3986 encoded 'string' back to unescaped version. 242 | * 243 | * @param string The data to be unescaped 244 | * @param olen unless NULL the length of the returned string is stored there. 245 | * @return decoded string or NULL 246 | * The caller must free the returned string. 247 | */ 248 | char *oauth_url_unescape(const char *string, size_t *olen) { 249 | size_t alloc, strindex=0; 250 | char *ns = NULL; 251 | unsigned char in; 252 | long hex; 253 | 254 | if (!string) return NULL; 255 | alloc = strlen(string)+1; 256 | ns = (char*) xmalloc(alloc); 257 | 258 | while(--alloc > 0) { 259 | in = *string; 260 | if(('%' == in) && ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { 261 | char hexstr[3]; // '%XX' 262 | hexstr[0] = string[1]; 263 | hexstr[1] = string[2]; 264 | hexstr[2] = 0; 265 | hex = strtol(hexstr, NULL, 16); 266 | in = (unsigned char)hex; /* hex is always < 256 */ 267 | string+=2; 268 | alloc-=2; 269 | } 270 | ns[strindex++] = in; 271 | string++; 272 | } 273 | ns[strindex]=0; 274 | if(olen) *olen = strindex; 275 | return ns; 276 | } 277 | 278 | /** 279 | * returns plaintext signature for the given key. 280 | * 281 | * the returned string needs to be freed by the caller 282 | * 283 | * @param m message to be signed 284 | * @param k key used for signing 285 | * @return signature string 286 | */ 287 | char *oauth_sign_plaintext (const char *m, const char *k) { 288 | return(strdup(k)); 289 | } 290 | 291 | /** 292 | * encode strings and concatenate with '&' separator. 293 | * The number of strings to be concatenated must be 294 | * given as first argument. 295 | * all arguments thereafter must be of type (char *) 296 | * 297 | * @param len the number of arguments to follow this parameter 298 | * @param ... string to escape and added (may be NULL) 299 | * 300 | * @return pointer to memory holding the concatenated 301 | * strings - needs to be free(d) by the caller. or NULL 302 | * in case we ran out of memory. 303 | */ 304 | char *oauth_catenc(int len, ...) { 305 | va_list va; 306 | int i; 307 | char *rv = (char*) xmalloc(sizeof(char)); 308 | *rv='\0'; 309 | va_start(va, len); 310 | for(i=0;i0)?1:0); 317 | if(rv) len+=strlen(rv); 318 | rv=(char*) xrealloc(rv,len*sizeof(char)); 319 | 320 | if(i>0) strcat(rv, "&"); 321 | strcat(rv, enc); 322 | free(enc); 323 | } 324 | va_end(va); 325 | return(rv); 326 | } 327 | 328 | /** 329 | * splits the given url into a parameter array. 330 | * (see \ref oauth_serialize_url and \ref oauth_serialize_url_parameters for the reverse) 331 | * 332 | * NOTE: Request-parameters-values may include an ampersand character. 333 | * However if unescaped this function will use them as parameter delimiter. 334 | * If you need to make such a request, this function since version 0.3.5 allows 335 | * to use the ASCII SOH (0x01) character as alias for '&' (0x26). 336 | * (the motivation is convenience: SOH is /untypeable/ and much more 337 | * unlikely to appear than '&' - If you plan to sign fancy URLs you 338 | * should not split a query-string, but rather provide the parameter array 339 | * directly to \ref oauth_serialize_url) 340 | * 341 | * @param url the url or query-string to parse. 342 | * @param argv pointer to a (char *) array where the results are stored. 343 | * The array is re-allocated to match the number of parameters and each 344 | * parameter-string is allocated with strdup. - The memory needs to be freed 345 | * by the caller. 346 | * @param qesc use query parameter escape (vs post-param-escape) - if set 347 | * to 1 all '+' are treated as spaces ' ' 348 | * 349 | * @return number of parameter(s) in array. 350 | */ 351 | int oauth_split_post_paramters(const char *url, char ***argv, short qesc) { 352 | int argc=0; 353 | char *token, *tmp, *t1; 354 | if (!argv) return 0; 355 | if (!url) return 0; 356 | t1=xstrdup(url); 357 | 358 | // '+' represents a space, in a URL query string 359 | while ((qesc&1) && (tmp=strchr(t1,'+'))) *tmp=' '; 360 | 361 | tmp=t1; 362 | while((token=strtok(tmp,"&?"))) { 363 | if(!strncasecmp("oauth_signature=",token,16)) continue; 364 | (*argv)=(char**) xrealloc(*argv,sizeof(char*)*(argc+1)); 365 | while (!(qesc&2) && (tmp=strchr(token,'\001'))) *tmp='&'; 366 | if (argc>0 || (qesc&4)) 367 | (*argv)[argc]=oauth_url_unescape(token, NULL); 368 | else 369 | (*argv)[argc]=xstrdup(token); 370 | if (argc==0 && strstr(token, ":/")) { 371 | // HTTP does not allow empty absolute paths, so the URL 372 | // 'http://example.com' is equivalent to 'http://example.com/' and should 373 | // be treated as such for the purposes of OAuth signing (rfc2616, section 3.2.1) 374 | // see http://groups.google.com/group/oauth/browse_thread/thread/c44b6f061bfd98c?hl=en 375 | char *slash=strstr(token, ":/"); 376 | while (slash && *(++slash) == '/') ; // skip slashes eg /xxx:[\/]*/ 377 | #if 0 378 | // skip possibly unescaped slashes in the userinfo - they're not allowed by RFC2396 but have been seen. 379 | // the hostname/IP may only contain alphanumeric characters - so we're safe there. 380 | if (slash && strchr(slash,'@')) slash=strchr(slash,'@'); 381 | #endif 382 | if (slash && !strchr(slash,'/')) { 383 | #ifdef DEBUG_OAUTH 384 | fprintf(stderr, "\nliboauth: added trailing slash to URL: '%s'\n\n", token); 385 | #endif 386 | free((*argv)[argc]); 387 | (*argv)[argc]= (char*) xmalloc(sizeof(char)*(2+strlen(token))); 388 | strcpy((*argv)[argc],token); 389 | strcat((*argv)[argc],"/"); 390 | } 391 | } 392 | if (argc==0 && (tmp=strstr((*argv)[argc],":80/"))) { 393 | memmove(tmp, tmp+3, strlen(tmp+2)); 394 | } 395 | tmp=NULL; 396 | argc++; 397 | } 398 | 399 | free(t1); 400 | return argc; 401 | } 402 | 403 | int oauth_split_url_parameters(const char *url, char ***argv) { 404 | return oauth_split_post_paramters(url, argv, 1); 405 | } 406 | 407 | /** 408 | * build a url query string from an array. 409 | * 410 | * @param argc the total number of elements in the array 411 | * @param start element in the array at which to start concatenating. 412 | * @param argv parameter-array to concatenate. 413 | * @return url string needs to be freed by the caller. 414 | * 415 | */ 416 | char *oauth_serialize_url (int argc, int start, char **argv) { 417 | return oauth_serialize_url_sep( argc, start, argv, "&", 0); 418 | } 419 | 420 | /** 421 | * encode query parameters from an array. 422 | * 423 | * @param argc the total number of elements in the array 424 | * @param start element in the array at which to start concatenating. 425 | * @param argv parameter-array to concatenate. 426 | * @param sep separator for parameters (usually "&") 427 | * @param mod - bitwise modifiers: 428 | * 1: skip all values that start with "oauth_" 429 | * 2: skip all values that don't start with "oauth_" 430 | * 4: add double quotation marks around values (use with sep=", " to generate HTTP Authorization header). 431 | * @return url string needs to be freed by the caller. 432 | */ 433 | char *oauth_serialize_url_sep (int argc, int start, char **argv, char *sep, int mod) { 434 | char *tmp, *t1; 435 | int i; 436 | int first=1; 437 | int seplen=strlen(sep); 438 | char *query = (char*) xmalloc(sizeof(char)); 439 | *query='\0'; 440 | for(i=start; i< argc; i++) { 441 | int len = 0; 442 | if ((mod&1)==1 && (strncmp(argv[i],"oauth_",6) == 0 || strncmp(argv[i],"x_oauth_",8) == 0) ) continue; 443 | if ((mod&2)==2 && (strncmp(argv[i],"oauth_",6) != 0 && strncmp(argv[i],"x_oauth_",8) != 0) && i!=0) continue; 444 | 445 | if (query) len+=strlen(query); 446 | 447 | if (i==start && i==0 && strstr(argv[i], ":/")) { 448 | tmp=xstrdup(argv[i]); 449 | #if 1 // encode white-space in the base-url 450 | while ((t1=strchr(tmp,' '))) { 451 | # if 0 452 | *t1='+'; 453 | # else 454 | size_t off = t1-tmp; 455 | char *t2 = (char*) xmalloc(sizeof(char)*(3+strlen(tmp))); 456 | strcpy(t2, tmp); 457 | strcpy(t2+off+2, tmp+off); 458 | *(t2+off)='%'; *(t2+off+1)='2'; *(t2+off+2)='0'; 459 | free(tmp); 460 | tmp=t2; 461 | # endif 462 | #endif 463 | } 464 | len+=strlen(tmp); 465 | } else if(!(t1=strchr(argv[i], '='))) { 466 | // see http://oauth.net/core/1.0/#anchor14 467 | // escape parameter names and arguments but not the '=' 468 | tmp=xstrdup(argv[i]); 469 | tmp=(char*) xrealloc(tmp,(strlen(tmp)+2)*sizeof(char)); 470 | strcat(tmp,"="); 471 | len+=strlen(tmp); 472 | } else { 473 | *t1=0; 474 | tmp = oauth_url_escape(argv[i]); 475 | *t1='='; 476 | t1 = oauth_url_escape((t1+1)); 477 | tmp=(char*) xrealloc(tmp,(strlen(tmp)+strlen(t1)+2+(mod&4?2:0))*sizeof(char)); 478 | strcat(tmp,"="); 479 | if (mod&4) strcat(tmp,"\""); 480 | strcat(tmp,t1); 481 | if (mod&4) strcat(tmp,"\""); 482 | free(t1); 483 | len+=strlen(tmp); 484 | } 485 | len+=seplen+1; 486 | query=(char*) xrealloc(query,len*sizeof(char)); 487 | strcat(query, ((i==start||first)?"":sep)); 488 | first=0; 489 | strcat(query, tmp); 490 | if (i==start && i==0 && strstr(tmp, ":/")) { 491 | strcat(query, "?"); 492 | first=1; 493 | } 494 | free(tmp); 495 | } 496 | return (query); 497 | } 498 | 499 | /** 500 | * build a query parameter string from an array. 501 | * 502 | * This function is a shortcut for \ref oauth_serialize_url (argc, 1, argv). 503 | * It strips the leading host/path, which is usually the first 504 | * element when using oauth_split_url_parameters on an URL. 505 | * 506 | * @param argc the total number of elements in the array 507 | * @param argv parameter-array to concatenate. 508 | * @return url string needs to be freed by the caller. 509 | */ 510 | char *oauth_serialize_url_parameters (int argc, char **argv) { 511 | return oauth_serialize_url(argc, 1, argv); 512 | } 513 | 514 | /** 515 | * generate a random string between 15 and 32 chars length 516 | * and return a pointer to it. The value needs to be freed by the 517 | * caller 518 | * 519 | * @return zero terminated random string. 520 | */ 521 | #if !defined HAVE_OPENSSL_HMAC_H && !defined USE_NSS 522 | /* pre liboauth-0.7.2 and possible future versions that don't use OpenSSL or NSS */ 523 | char *oauth_gen_nonce() { 524 | char *nc; 525 | static int rndinit = 1; 526 | const char *chars = "abcdefghijklmnopqrstuvwxyz" 527 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789_"; 528 | unsigned int max = strlen( chars ); 529 | int i, len; 530 | 531 | if(rndinit) {srand(time(NULL) 532 | #ifndef WIN32 // quick windows check. 533 | * getpid() 534 | #endif 535 | ); rndinit=0;} // seed random number generator - FIXME: we can do better ;) 536 | 537 | len=15+floor(rand()*16.0/(double)RAND_MAX); 538 | nc = (char*) xmalloc((len+1)*sizeof(char)); 539 | for(i=0;i 557 | # define MY_RAND RAND_bytes 558 | # define MY_SRAND ; 559 | #endif 560 | char *oauth_gen_nonce() { 561 | char *nc; 562 | unsigned char buf; 563 | const char *chars = "abcdefghijklmnopqrstuvwxyz" 564 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789_"; 565 | unsigned int max = strlen(chars); 566 | int i, len; 567 | 568 | MY_SRAND 569 | MY_RAND(&buf, 1); 570 | len=15+(((short)buf)&0x0f); 571 | nc = (char*) xmalloc((len+1)*sizeof(char)); 572 | for(i=0;il && !strncmp(argv[i],key,l) && argv[i][l] == '=') return 1; 638 | return 0; 639 | } 640 | 641 | /** 642 | * add query parameter to array 643 | * 644 | * @param argcp pointer to array length int 645 | * @param argvp pointer to array values 646 | * @param addparam parameter to add (eg. "foo=bar") 647 | */ 648 | void oauth_add_param_to_array(int *argcp, char ***argvp, const char *addparam) { 649 | (*argvp)=(char**) xrealloc(*argvp,sizeof(char*)*((*argcp)+1)); 650 | (*argvp)[(*argcp)++]= (char*) xstrdup(addparam); 651 | } 652 | 653 | /** 654 | * 655 | */ 656 | void oauth_add_protocol(int *argcp, char ***argvp, 657 | OAuthMethod method, 658 | const char *c_key, //< consumer key - posted plain text 659 | const char *t_key //< token key - posted plain text in URL 660 | ){ 661 | char oarg[1024]; 662 | 663 | // add OAuth specific arguments 664 | if (!oauth_param_exists(*argvp,*argcp,"oauth_nonce")) { 665 | char *tmp; 666 | snprintf(oarg, 1024, "oauth_nonce=%s", (tmp=oauth_gen_nonce())); 667 | oauth_add_param_to_array(argcp, argvp, oarg); 668 | free(tmp); 669 | } 670 | 671 | if (!oauth_param_exists(*argvp,*argcp,"oauth_timestamp")) { 672 | snprintf(oarg, 1024, "oauth_timestamp=%li", (long int) time(NULL)); 673 | oauth_add_param_to_array(argcp, argvp, oarg); 674 | } 675 | 676 | if (t_key) { 677 | snprintf(oarg, 1024, "oauth_token=%s", t_key); 678 | oauth_add_param_to_array(argcp, argvp, oarg); 679 | } 680 | 681 | snprintf(oarg, 1024, "oauth_consumer_key=%s", c_key); 682 | oauth_add_param_to_array(argcp, argvp, oarg); 683 | 684 | snprintf(oarg, 1024, "oauth_signature_method=%s", 685 | method==0?"HMAC-SHA1":method==1?"RSA-SHA1":"PLAINTEXT"); 686 | oauth_add_param_to_array(argcp, argvp, oarg); 687 | 688 | if (!oauth_param_exists(*argvp,*argcp,"oauth_version")) { 689 | snprintf(oarg, 1024, "oauth_version=1.0"); 690 | oauth_add_param_to_array(argcp, argvp, oarg); 691 | } 692 | 693 | #if 0 // oauth_version 1.0 Rev A 694 | if (!oauth_param_exists(argv,argc,"oauth_callback")) { 695 | snprintf(oarg, 1024, "oauth_callback=oob"); 696 | oauth_add_param_to_array(argcp, argvp, oarg); 697 | } 698 | #endif 699 | 700 | } 701 | 702 | char *oauth_sign_url (const char *url, char **postargs, 703 | OAuthMethod method, 704 | const char *c_key, //< consumer key - posted plain text 705 | const char *c_secret, //< consumer secret - used as 1st part of secret-key 706 | const char *t_key, //< token key - posted plain text in URL 707 | const char *t_secret //< token secret - used as 2st part of secret-key 708 | ) { 709 | return oauth_sign_url2(url, postargs, 710 | method, NULL, 711 | c_key, c_secret, 712 | t_key, t_secret); 713 | } 714 | 715 | char *oauth_sign_url2 (const char *url, char **postargs, 716 | OAuthMethod method, 717 | const char *http_method, //< HTTP request method 718 | const char *c_key, //< consumer key - posted plain text 719 | const char *c_secret, //< consumer secret - used as 1st part of secret-key 720 | const char *t_key, //< token key - posted plain text in URL 721 | const char *t_secret //< token secret - used as 2st part of secret-key 722 | ) { 723 | int argc; 724 | char **argv = NULL; 725 | char *rv; 726 | 727 | if (postargs) 728 | argc = oauth_split_post_paramters(url, &argv, 0); 729 | else 730 | argc = oauth_split_url_parameters(url, &argv); 731 | 732 | rv=oauth_sign_array2(&argc, &argv, postargs, 733 | method, http_method, 734 | c_key, c_secret, t_key, t_secret); 735 | 736 | oauth_free_array(&argc, &argv); 737 | return(rv); 738 | } 739 | 740 | char *oauth_sign_array (int *argcp, char***argvp, 741 | char **postargs, 742 | OAuthMethod method, 743 | const char *c_key, //< consumer key - posted plain text 744 | const char *c_secret, //< consumer secret - used as 1st part of secret-key 745 | const char *t_key, //< token key - posted plain text in URL 746 | const char *t_secret //< token secret - used as 2st part of secret-key 747 | ) { 748 | return oauth_sign_array2 (argcp, argvp, 749 | postargs, method, 750 | NULL, 751 | c_key, c_secret, 752 | t_key, t_secret); 753 | } 754 | 755 | void oauth_sign_array2_process (int *argcp, char***argvp, 756 | char **postargs, 757 | OAuthMethod method, 758 | const char *http_method, //< HTTP request method 759 | const char *c_key, //< consumer key - posted plain text 760 | const char *c_secret, //< consumer secret - used as 1st part of secret-key 761 | const char *t_key, //< token key - posted plain text in URL 762 | const char *t_secret //< token secret - used as 2st part of secret-key 763 | ) { 764 | char oarg[1024]; 765 | char *query; 766 | char *okey, *odat, *sign; 767 | char *http_request_method; 768 | 769 | if (!http_method) { 770 | http_request_method = xstrdup(postargs?"POST":"GET"); 771 | } else { 772 | int i; 773 | http_request_method = xstrdup(http_method); 774 | for (i=0;i