-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathproxysocks.hpp
1329 lines (1247 loc) · 46.8 KB
/
proxysocks.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//
// Created by imper on 3/4/22.
//
#ifndef PORT_FLOODER_PROXYSOCKS_HPP
#define PORT_FLOODER_PROXYSOCKS_HPP
///// DECL BEGIN /////
#include <cstdint>
/*! \cond PRIVATE */
# define DLL_EXPORT_PROXYSOCKET
# include <netinet/in.h>
# define SOCKET int
# ifndef INVALID_SOCKET
# define INVALID_SOCKET -1
# endif
/*! \endcond */
/*! \brief proxy types
* \sa proxysocketconfig_create()
* \sa proxysocketconfig_add_proxy()
* \{
*/
/*! \brief direct connection without proxy */
#define PROXYSOCKET_TYPE_NONE 0x00
/*! \brief SOCKS4 proxy */
#define PROXYSOCKET_TYPE_SOCKS4 0x04
/*! \brief SOCKS5 proxy */
#define PROXYSOCKET_TYPE_SOCKS5 0x05
/*! \brief HTTP proxy */
#define PROXYSOCKET_TYPE_WEB_CONNECT 0x20
/*! \brief invalid proxy type */
#define PROXYSOCKET_TYPE_INVALID -1
/*! @} */
/*! \brief logging levels
* \sa proxysocketconfig_log_fn()
* \name PROXYSOCKET_VERSION_*
* \{
*/
#define PROXYSOCKET_LOG_ERROR 0
#define PROXYSOCKET_LOG_WARNING 1
#define PROXYSOCKET_LOG_INFO 2
#define PROXYSOCKET_LOG_DEBUG 3
/*! @} */
namespace net
{
//goal: function to create a socket that can be used by system function and connect it to a remote host, optionally through a proxy
/*! \brief get the textual name of a proxy type
* \param proxytype proxy type (one of the PROXYSOCKET_TYPE_ constants)
* \return name of the proxy type (NONE/SOCKS4A/SOCKS5/WEB/INVALID)
* \sa proxysocketconfig_create()
* \sa proxysocketconfig_get_name_type()
*/
DLL_EXPORT_PROXYSOCKET const char* proxysocketconfig_get_type_name(int proxytype);
/*! \brief get the textual name of a proxy type
* \param proxytypename name of the proxy type (NONE/DIRECT/SOCKS4/SOCKS4A/SOCKS5/WEB/HTTP)
* \return proxy type (one of the PROXYSOCKET_TYPE_ constants) or PROXYSOCKET_TYPE_INVALID on failure
* \sa proxysocketconfig_create()
* \sa proxysocketconfig_get_type_name()
*/
DLL_EXPORT_PROXYSOCKET int proxysocketconfig_get_name_type(const char* proxytypename);
/*! \brief proxysocketconfig object type */
typedef struct proxysocketconfig_struct* proxysocketconfig;
/*! \brief create proxy information data structure set up for direct connection
* \return proxy information data structure on success or NULL on failure
* \sa proxysocketconfig_create()
* \sa proxysocketconfig_add_proxy()
* \sa proxysocketconfig_free()
*/
DLL_EXPORT_PROXYSOCKET proxysocketconfig proxysocketconfig_create_direct();
/*! \brief create proxy information data structure
* \param proxytype proxy type (one of the PROXYSOCKET_TYPE_ constants)
* \param proxyhost proxy hostname or IP address (or when proxytype is PROXYSOCKET_TYPE_NONE address to bind to if non-zero)
* \param proxyport proxy port number (or when proxytype is PROXYSOCKET_TYPE_NONE port to bind to if non-zero)
* \param proxyuser proxy authentication login or NULL for none
* \param proxypass proxy authentication password or NULL for none
* \return proxy information data structure on success or NULL on failure
* \sa proxysocketconfig_create_direct()
* \sa proxysocketconfig_add_proxy()
* \sa proxysocketconfig_free()
*/
DLL_EXPORT_PROXYSOCKET proxysocketconfig proxysocketconfig_create(
int proxytype, const char* proxyhost, uint16_t proxyport, const char* proxyuser, const char* proxypass);
/*! \brief add a proxy method to proxy information data structure (multiple can be daisy chained)
* \param proxy proxy information as returned by proxysocketconfig_create()
* \param proxytype proxy type (one of the PROXYSOCKET_TYPE_ constants)
* \param proxyhost proxy hostname or IP address (or when proxytype is PROXYSOCKET_TYPE_NONE address to bind to if non-zero)
* \param proxyport proxy port number (or when proxytype is PROXYSOCKET_TYPE_NONE port to bind to if non-zero)
* \param proxyuser proxy authentication login or NULL for none
* \param proxypass proxy authentication password or NULL for none
* \return proxy information data structure on success or NULL on failure
* \sa proxysocketconfig_create_direct()
* \sa proxysocketconfig_create()
* \sa proxysocketconfig_free()
*/
DLL_EXPORT_PROXYSOCKET int proxysocketconfig_add_proxy(
proxysocketconfig proxy, int proxytype, const char* proxyhost, uint16_t proxyport, const char* proxyuser, const char* proxypass);
/*! \brief type of pointer to function for logging
* \param level logging level (one of the PROXYSOCKET_LOG_ constants)
* \param message text to be logged
* \param userdata custom data as passed to proxysocketconfig_set_logging
* \sa proxysocketconfig_set_logging()
*/
typedef void (* proxysocketconfig_log_fn)(int level, const char* message, void* userdata);
/*! \brief get description of configured proxy
* \param proxy proxy information as returned by proxysocketconfig_create()
* \return string with description of proxy settings (caller must free) or NULL on failure
*/
DLL_EXPORT_PROXYSOCKET char* proxysocketconfig_get_description(proxysocketconfig proxy);
/*! \brief configure logging function
* \param proxy proxy information as returned by proxysocketconfig_create()
* \param log_fn logging function to use or NULL to disable logging
* \param userdata user defined data that will be passed to the logging function
* \sa proxysocketconfig_create()
* \sa proxysocketconfig_log_fn
*/
DLL_EXPORT_PROXYSOCKET void proxysocketconfig_set_logging(proxysocketconfig proxy, proxysocketconfig_log_fn log_fn, void* userdata);
/*! \brief configure connection timeouts
* \param proxy proxy information as returned by proxysocketconfig_create()
* \param sendtimeout send timeout in milliseconds (0 for no timeout)
* \param recvtimeout send timeout in milliseconds (0 for no timeout)
* \sa proxysocketconfig_create()
*/
DLL_EXPORT_PROXYSOCKET void proxysocketconfig_set_timeout(proxysocketconfig proxy, uint32_t sendtimeout, uint32_t recvtimeout);
/*! \brief specify where name resolution is done
* \param proxy proxy information as returned by proxysocketconfig_create()
* \param proxy_dns perform DNS lookup on the proxy server if non-zero or on client if zero (default)
* \sa proxysocketconfig_create()
*/
DLL_EXPORT_PROXYSOCKET void proxysocketconfig_use_proxy_dns(proxysocketconfig proxy, int proxy_dns);
/*! \brief clean up proxy information
* \param proxy proxy information as returned by proxysocketconfig_create()
* \sa proxysocketconfig_create()
*/
DLL_EXPORT_PROXYSOCKET void proxysocketconfig_free(proxysocketconfig proxy);
/*! \brief establish a TCP connection using the specified proxy
* \param proxy proxy information as returned by proxysocketconfig_create()
* \param dsthost destination hostname or IP address
* \param dstport destination port number
* \param errmsg pointer to string that will receive error message, can be NULL, caller must free
* \return network socket on success or INVALID_SOCKET on failure
* \sa proxysocketconfig_create()
*/
DLL_EXPORT_PROXYSOCKET SOCKET proxysocket_connect(proxysocketconfig proxy, const char* dsthost, uint16_t dstport, char** errmsg);
/*! \brief disconnect a proxy socket
* \param proxy proxy information as returned by proxysocketconfig_create()
* \param sock network socket as returned by proxysocket_connect()
* \sa proxysocketconfig_create()
* \sa proxysocket_connect()
*/
DLL_EXPORT_PROXYSOCKET void proxysocket_disconnect(proxysocketconfig proxy, SOCKET sock);
/*! \brief set socket timeout
* \param sock network socket as returned by proxysocket_connect() or socket()
* \param timeout timeout in seconds (0 to disable)
* \sa proxysocket_connect()
*/
DLL_EXPORT_PROXYSOCKET void socket_set_timeouts_milliseconds(SOCKET sock, uint32_t sendtimeout, uint32_t recvtimeout);
/*! \brief read a line from a socket
* \param sock network socket as returned by proxysocket_connect() or socket()
* \return contents of line without trailing new line (caller must free) or NULL on failure
* \sa proxysocket_connect()
*/
DLL_EXPORT_PROXYSOCKET char* socket_receiveline(SOCKET sock);
/*! \brief get error message of last socket error
* \return error message or NULL if no error
*/
DLL_EXPORT_PROXYSOCKET char* socket_get_error_message();
}
///// DECL END /////
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <cerrno>
#ifndef SOCKET_ERROR
# define SOCKET_ERROR (-1)
#endif
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cstdarg>
#define USE_CLIENT_DNS 0
#define USE_PROXY_DNS 1
namespace net
{
struct proxysocketconfig_struct
{
struct proxyinfo_struct* proxyinfolist;
proxysocketconfig_log_fn log_function;
void* log_data;
int8_t proxy_dns;
uint32_t sendtimeout;
uint32_t recvtimeout;
};
struct proxyinfo_struct
{
int proxytype;
char* proxyhost;
uint16_t proxyport;
char* proxyuser;
char* proxypass;
struct proxyinfo_struct* next;
};
#ifndef HAVE_VASPRINTF
int vasprintf(char** strp, const char* fmt, va_list ap)
{
va_list aq;
int len;
//allocate buffer for logging data
va_copy(aq, ap);
len = vsnprintf(nullptr, 0, fmt, aq);
if ((*strp = (char*)malloc(len + 1)) == nullptr)
{
return -1;
}
//format logging data
vsnprintf(*strp, len + 1, fmt, ap);
return len;
}
#endif
#ifndef HAVE_ASPRINTF
int asprintf(char** strp, const char* fmt, ...)
{
int len;
va_list ap;
va_start(ap, fmt);
len = vasprintf(strp, fmt, ap);
va_end(ap);
return len;
}
#endif
int appendsprintf(char** dststrp, int dststrlen, const char* fmt, ...)
{
int len;
char* str;
va_list ap;
va_start(ap, fmt);
len = vasprintf(&str, fmt, ap);
va_end(ap);
//append result
if (len >= 0)
{
if (dststrlen < 0)
dststrlen = (*dststrp ? strlen(*dststrp) : 0);
len = dststrlen + len;
if ((*dststrp = static_cast<char*>(realloc(*dststrp, len + 1))) == nullptr)
len = -1;
else
strcpy(*dststrp + dststrlen, str);
free(str);
}
return len;
}
////////////////////////////////////////////////////////////////////////
static const char* memory_allocation_error = "Memory allocation error";
uint32_t get_ipv4_address(const char* hostname)
{
uint32_t addr;
struct hostent* hostdata;
if (!hostname || !*hostname)
return INADDR_NONE;
//check if host is dotted IP address
if ((addr = inet_addr(hostname)) != INADDR_NONE)
return addr;
//look up hostname (this will block), note: gethostbyname() is not thread-safe
if ((hostdata = gethostbyname(hostname)) != nullptr)
memcpy(&addr, hostdata->h_addr_list[0], sizeof(addr));
else
addr = INADDR_NONE;
return addr;
}
char* make_base64_string(const char* str)
{
int done = 0;
char base64_table[256];
int i;
char* buf = (char*)malloc((strlen(str) + 2) / 3 * 4 + 1);
char* dst = buf;
const char* src = str;
//fill base64_table with character encodings
for (i = 0; i < 26; i++)
{
base64_table[i] = (char)('A' + i);
base64_table[26 + i] = (char)('a' + i);
}
for (i = 0; i < 10; i++)
{
base64_table[52 + i] = (char)('0' + i);
}
base64_table[62] = '+';
base64_table[63] = '/';
//encode data
while (!done)
{
char igroup[3];
int n;
//read input
igroup[0] = igroup[1] = igroup[2] = 0;
for (n = 0; n < 3; n++)
{
if (!*src)
{
done++;
break;
}
igroup[n] = *src++;
}
//code
if (n > 0)
{
*dst++ = base64_table[igroup[0] >> 2];
*dst++ = base64_table[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
*dst++ = (n < 2 ? '=' : base64_table[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)]);
*dst++ = (n < 3 ? '=' : base64_table[igroup[2] & 0x3F]);
}
}
*dst = 0;
return buf;
}
int send_http_request(SOCKET sock, const char* request, char** response)
{
char* responseline;
int responselinelen;
int responselen = 0;
int resultcode = -1;
char* p;
if (response)
*response = nullptr;
//send request
if (request)
{
if (send(sock, request, strlen(request), 0) == -1)
return -1;
}
//get result code
if ((responseline = socket_receiveline(sock)) == nullptr)
return -1;
if (strncasecmp(responseline, "HTTP/", 5) != 0)
return -1;
p = responseline + 5;
while (*p && (isdigit(*p) || *p == '.'))
p++;
if (!*p || *p++ != ' ')
return -1;
if ((resultcode = strtol(p, &p, 10)) == 0)
return -1;
//get entire response header
while (responseline && *responseline)
{
if (response)
{
responselinelen = strlen(responseline);
*response = (char*)realloc(*response, responselen + responselinelen + 2);
memcpy(*response + responselen, responseline, responselinelen);
responselen += responselinelen;
(*response)[responselen++] = '\n';
}
free(responseline);
responseline = socket_receiveline(sock);
}
if (responseline)
free(responseline);
if (response)
(*response)[responselen] = 0;
return resultcode;
}
////////////////////////////////////////////////////////////////////////
/* * * definitions needed for SOCKS4 proxy client * * */
#define SOCKS4_VERSION 0x04
#define SOCKS4_COMMAND_CONNECT 0x01
//#define SOCKS4_COMMAND_BIND 0x02
#define SOCKS4_STATUS_SUCCESS 90
#define SOCKS4_STATUS_FAILED 91
#define SOCKS4_STATUS_IDENT_FAILED 92
#define SOCKS4_STATUS_IDENT_MISMATCH 93
//should compile with -mno-ms-bitfields
//#pragma pack(1)
struct __attribute__((packed, aligned(1))) socks4_connect_request
{
uint8_t socks_version;
uint8_t socks_command;
uint16_t dst_port;
uint32_t dst_addr;
uint8_t userid[1];
};
/* * * definitions needed for SOCKS5 proxy client * * */
#define SOCKS5_VERSION 0x05
#define SOCKS5_METHOD_NOAUTH 0x00
//#define SOCKS5_METHOD_GSSAPI 0x01
#define SOCKS5_METHOD_LOGIN 0x02
#define SOCKS5_METHOD_NONE 0xFF
#define SOCKS5_COMMAND_CONNECT 0x01
//#define SOCKS5_COMMAND_BIND 0x02
#define SOCKS5_ADDRESSTYPE_IPV4 0x01
#define SOCKS5_ADDRESSTYPE_DOMAINNAME 0x03
#define SOCKS5_ADDRESSTYPE_IPV6 0x04
#define SOCKS5_STATUS_SUCCESS 0x00
#define SOCKS5_STATUS_SOCKS_SERVER_FAILURE 0x01
#define SOCKS5_STATUS_DENIED 0x02
#define SOCKS5_STATUS_NETWORK_UNREACHABLE 0x03
#define SOCKS5_STATUS_HOST_UNREACHABLE 0x04
#define SOCKS5_STATUS_CONNECTION_REFUSED 0x05
#define SOCKS5_STATUS_TTL_EXPIRES 0x06
#define SOCKS5_STATUS_COMMAND_NOT_SUPPORTED 0x07
#define SOCKS5_STATUS_ADDRESS_TYPE_NOT_SUPPORTED 0x08
//should compile with -mno-ms-bitfields
//#pragma pack(1)
struct __attribute__((packed, aligned(1))) socks5_connect_request_ipv4
{
uint8_t socks_version;
uint8_t socks_command;
uint8_t reserved;
uint8_t socks_addresstype;
uint32_t dst_addr;
uint16_t dst_port;
};
void write_log_info(proxysocketconfig proxy, int level, const char* fmt, ...)
{
if (proxy && proxy->log_function)
{
va_list ap;
char* msg;
int msglen;
va_start(ap, fmt);
msglen = vasprintf(&msg, fmt, ap);
va_end(ap);
if (msglen < 0)
{
proxy->log_function(level, memory_allocation_error, proxy->log_data);
return;
}
//pass logging data to user function
proxy->log_function(level, msg, proxy->log_data);
//clean up
free(msg);
va_end(ap);
}
}
void proxyinfolist_free(struct proxyinfo_struct* proxyinfo)
{
struct proxyinfo_struct* next;
struct proxyinfo_struct* current = proxyinfo;
while (current)
{
next = current->next;
if (current->proxyhost)
free(current->proxyhost);
if (current->proxyuser)
free(current->proxyuser);
if (current->proxypass)
free(current->proxypass);
free(current);
current = next;
}
}
DLL_EXPORT_PROXYSOCKET const char* proxysocketconfig_get_type_name(int proxytype)
{
switch (proxytype)
{
case PROXYSOCKET_TYPE_NONE:
return "NONE";
case PROXYSOCKET_TYPE_SOCKS4:
return "SOCKS4A";
case PROXYSOCKET_TYPE_SOCKS5:
return "SOCKS5";
case PROXYSOCKET_TYPE_WEB_CONNECT:
return "WEB";
default:
return "INVALID";
}
}
DLL_EXPORT_PROXYSOCKET int proxysocketconfig_get_name_type(const char* proxytypename)
{
if (strcasecmp(proxytypename, "NONE") == 0 || strcasecmp(proxytypename, "DIRECT") == 0)
return PROXYSOCKET_TYPE_NONE;
if (strcasecmp(proxytypename, "SOCKS4A") == 0 || strcasecmp(proxytypename, "SOCKS4") == 0)
return PROXYSOCKET_TYPE_SOCKS4;
if (strcasecmp(proxytypename, "SOCKS5") == 0)
return PROXYSOCKET_TYPE_SOCKS5;
if (strcasecmp(proxytypename, "WEB") == 0 || strcasecmp(proxytypename, "HTTP") == 0)
return PROXYSOCKET_TYPE_WEB_CONNECT;
return PROXYSOCKET_TYPE_INVALID;
}
DLL_EXPORT_PROXYSOCKET proxysocketconfig proxysocketconfig_create_direct()
{
struct proxysocketconfig_struct* proxy;
if ((proxy = (struct proxysocketconfig_struct*)malloc(sizeof(struct proxysocketconfig_struct))) == nullptr)
return nullptr;
proxy->proxyinfolist = nullptr;
proxy->log_function = nullptr;
proxy->log_data = nullptr;
proxy->proxy_dns = USE_CLIENT_DNS;
proxy->sendtimeout = 0;
proxy->recvtimeout = 0;
if (proxysocketconfig_add_proxy(proxy, PROXYSOCKET_TYPE_NONE, nullptr, 0, nullptr, nullptr) != 0)
{
free(proxy);
return nullptr;
}
return proxy;
}
DLL_EXPORT_PROXYSOCKET proxysocketconfig proxysocketconfig_create(
int proxytype, const char* proxyhost, uint16_t proxyport, const char* proxyuser, const char* proxypass)
{
struct proxysocketconfig_struct* proxy;
proxy = proxysocketconfig_create_direct();
if (proxysocketconfig_add_proxy(proxy, proxytype, proxyhost, proxyport, proxyuser, proxypass) != 0)
{
free(proxy);
return nullptr;
}
return proxy;
}
DLL_EXPORT_PROXYSOCKET int proxysocketconfig_add_proxy(
proxysocketconfig proxy, int proxytype, const char* proxyhost, uint16_t proxyport, const char* proxyuser, const char* proxypass)
{
//determine next entry (clean up and remove if a direct connection is inserted before)
struct proxyinfo_struct* next = proxy->proxyinfolist;
if (proxytype == PROXYSOCKET_TYPE_NONE && next)
{
proxyinfolist_free(next);
next = nullptr;
}
//insert new proxy in front of the list
if (!proxy || (proxy->proxyinfolist = (struct proxyinfo_struct*)malloc(sizeof(struct proxyinfo_struct))) == nullptr)
return -1;
proxy->proxyinfolist->proxytype = proxytype;
proxy->proxyinfolist->proxyhost = (proxyhost ? strdup(proxyhost) : nullptr);
proxy->proxyinfolist->proxyport = proxyport;
proxy->proxyinfolist->proxyuser = (proxyuser ? strdup(proxyuser) : nullptr);
proxy->proxyinfolist->proxypass = (proxypass ? strdup(proxypass) : nullptr);
proxy->proxyinfolist->next = next;
return 0;
}
char* proxysocketconfig_get_description_entry(proxysocketconfig proxy, struct proxyinfo_struct* proxyinfo, char* desc, int desclen)
{
if (!proxy || !proxyinfo)
return desc;
if (proxyinfo != proxy->proxyinfolist)
desclen = appendsprintf(&desc, desclen, " -> ");
switch (proxyinfo->proxytype)
{
case PROXYSOCKET_TYPE_NONE :
desclen = appendsprintf(&desc, desclen, "direct connection");
break;
case PROXYSOCKET_TYPE_SOCKS4 :
desclen = appendsprintf(
&desc, desclen, "SOCKS4 proxy: %s:%u (%s%s)", proxyinfo->proxyhost, (unsigned int)proxyinfo->proxyport,
(!proxyinfo->proxyuser || !*proxyinfo->proxyuser ? "no authentication" : "user: "),
(!proxyinfo->proxyuser || !*proxyinfo->proxyuser ? "" : proxyinfo->proxyuser));
break;
case PROXYSOCKET_TYPE_SOCKS5 :
desclen = appendsprintf(
&desc, desclen, "SOCKS5 proxy: %s:%u (%s%s)", proxyinfo->proxyhost, (unsigned int)proxyinfo->proxyport,
(!proxyinfo->proxyuser || !*proxyinfo->proxyuser ? "no authentication" : "user: "),
(!proxyinfo->proxyuser || !*proxyinfo->proxyuser ? "" : proxyinfo->proxyuser));
break;
case PROXYSOCKET_TYPE_WEB_CONNECT :
desclen = appendsprintf(
&desc, desclen, "web proxy: %s:%u (%s%s)", proxyinfo->proxyhost, (unsigned int)proxyinfo->proxyport,
(!proxyinfo->proxyuser || !*proxyinfo->proxyuser ? "no authentication" : "user: "),
(!proxyinfo->proxyuser || !*proxyinfo->proxyuser ? "" : proxyinfo->proxyuser));
break;
//case PROXYSOCKET_TYPE_INVALID :
default :
desclen = appendsprintf(&desc, desclen, "INVALID");
break;
}
if (!proxyinfo->next || proxyinfo->next->proxytype == PROXYSOCKET_TYPE_NONE)
return desc;
return
proxysocketconfig_get_description_entry(proxy, proxyinfo->next, desc, desclen);
}
DLL_EXPORT_PROXYSOCKET char* proxysocketconfig_get_description(proxysocketconfig proxy)
{
char* result = nullptr;
int resultlen = 0;
if (!proxy || !proxy->proxyinfolist)
return nullptr;
if (proxy->proxyinfolist && proxy->proxyinfolist->proxytype != PROXYSOCKET_TYPE_NONE)
resultlen = appendsprintf(&result, 0, "(use %s DNS) ", (proxy->proxy_dns == USE_PROXY_DNS ? "server" : "client"));
return
proxysocketconfig_get_description_entry(proxy, proxy->proxyinfolist, result, resultlen);
}
DLL_EXPORT_PROXYSOCKET void proxysocketconfig_set_logging(proxysocketconfig proxy, proxysocketconfig_log_fn log_fn, void* userdata)
{
proxy->log_function = log_fn;
proxy->log_data = userdata;
}
DLL_EXPORT_PROXYSOCKET void proxysocketconfig_set_timeout(proxysocketconfig proxy, uint32_t sendtimeout, uint32_t recvtimeout)
{
proxy->sendtimeout = sendtimeout;
proxy->recvtimeout = recvtimeout;
}
DLL_EXPORT_PROXYSOCKET void proxysocketconfig_use_proxy_dns(proxysocketconfig proxy, int proxy_dns)
{
proxy->proxy_dns = proxy_dns == 0 ? USE_CLIENT_DNS : USE_PROXY_DNS;
}
DLL_EXPORT_PROXYSOCKET void proxysocketconfig_free(proxysocketconfig proxy)
{
if (proxy)
{
proxyinfolist_free(proxy->proxyinfolist);
free(proxy);
}
}
void log_and_keep_error_message(proxysocketconfig proxy, char** pmsg, const char* fmt, ...)
{
if (fmt && (proxy->log_function || pmsg))
{
char* msg;
int msglen;
//generate message
va_list ap;
va_start(ap, fmt);
msglen = vasprintf(&msg, fmt, ap);
va_end(ap);
//log message
if (msglen >= 0)
{
if (proxy->log_function)
proxy->log_function(PROXYSOCKET_LOG_ERROR, msg, proxy->log_data);
if (pmsg)
*pmsg = msg;
else
free(msg);
}
}
}
#define ERROR_DISCONNECT_AND_ABORT(...) \
{ \
log_and_keep_error_message(proxy, errmsg, __VA_ARGS__); \
if (sock != INVALID_SOCKET) \
proxysocket_disconnect(proxy, sock); \
return INVALID_SOCKET; \
}
SOCKET proxyinfo_connect(proxysocketconfig proxy, struct proxyinfo_struct* proxyinfo, const char* dsthost, uint16_t dstport, char** errmsg)
{
uint32_t hostaddr;
uint32_t proxyaddr;
SOCKET sock = INVALID_SOCKET;
if (!proxyinfo)
ERROR_DISCONNECT_AND_ABORT("Proxy connection information missing")
//resolve destination host if needed (when client DNS is used or for a direct connection)
if (proxy->proxy_dns == USE_CLIENT_DNS || proxyinfo->proxytype == PROXYSOCKET_TYPE_NONE)
{
if ((hostaddr = get_ipv4_address(dsthost)) == INADDR_NONE)
ERROR_DISCONNECT_AND_ABORT("Error looking up host: %s", dsthost)
write_log_info(
proxy, PROXYSOCKET_LOG_DEBUG,
"Resolved host %s to IP: %s", dsthost, inet_ntoa(*(struct in_addr*)&hostaddr));
}
else
{
hostaddr = INADDR_NONE;
}
//resolve proxy host address
proxyaddr = INADDR_NONE;
if (proxyinfo->proxyhost && *proxyinfo->proxyhost)
{
if ((proxyaddr = get_ipv4_address(proxyinfo->proxyhost)) == INADDR_NONE)
ERROR_DISCONNECT_AND_ABORT("Error looking up proxy host: %s", proxyinfo->proxyhost)
write_log_info(
proxy, PROXYSOCKET_LOG_DEBUG, "Resolved proxy host %s to IP: %s",
proxyinfo->proxyhost, inet_ntoa(*(struct in_addr*)&proxyaddr));
}
if (proxyaddr == INADDR_NONE && proxyinfo->proxytype != PROXYSOCKET_TYPE_NONE)
ERROR_DISCONNECT_AND_ABORT("Missing proxy host")
if (proxyinfo->proxytype == PROXYSOCKET_TYPE_NONE)
{
/* * * DIRECT CONNECTION WITHOUT PROXY * * */
//create the socket
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
ERROR_DISCONNECT_AND_ABORT("Error creating connection socket")
/*
//set linger option
const struct linger option_linger = {~0, 3}; //linger 3 seconds when disconnecting
setsockopt(sock, SOL_SOCKET, SO_LINGER, (const char*)&option_linger, sizeof(option_linger));
*/
//bind the socket
if ((proxyaddr != INADDR_NONE && proxyaddr != INADDR_ANY) || proxyinfo->proxyport)
{
struct sockaddr_in local_sock_addr{ };
local_sock_addr.sin_family = AF_INET;
local_sock_addr.sin_port = htons(proxyinfo->proxyport);
local_sock_addr.sin_addr.s_addr = (proxyaddr == INADDR_NONE ? INADDR_ANY : proxyaddr);
write_log_info(
proxy, PROXYSOCKET_LOG_INFO,
"Binding to: %s:%lu", inet_ntoa(*(struct in_addr*)&local_sock_addr.sin_addr.s_addr),
(unsigned long)ntohs(local_sock_addr.sin_port));
if (bind(sock, (struct sockaddr*)&local_sock_addr, sizeof(local_sock_addr)) != 0)
ERROR_DISCONNECT_AND_ABORT("Error binding socket to: %s:%lu", inet_ntoa(*(struct in_addr*)&local_sock_addr.sin_addr.s_addr),
(unsigned long)ntohs(local_sock_addr.sin_port))
}
//set connection timeout
socket_set_timeouts_milliseconds(sock, proxy->sendtimeout, proxy->recvtimeout);
//connect to host
struct sockaddr_in remote_sock_addr{ };
remote_sock_addr.sin_family = AF_INET;
remote_sock_addr.sin_port = htons(dstport);
remote_sock_addr.sin_addr.s_addr = hostaddr;
write_log_info(
proxy, PROXYSOCKET_LOG_INFO,
"Connecting to host: %s:%lu", inet_ntoa(*(struct in_addr*)&remote_sock_addr.sin_addr.s_addr), (unsigned long)dstport
);
if (connect(sock, (struct sockaddr*)&remote_sock_addr, sizeof(remote_sock_addr)) == SOCKET_ERROR)
ERROR_DISCONNECT_AND_ABORT("Error connecting to host: %s:%lu", inet_ntoa(*(struct in_addr*)&hostaddr), (unsigned long)dstport)
}
else if (proxyinfo->proxytype == PROXYSOCKET_TYPE_SOCKS4)
{
/* * * CONNECTION USING SOCKS4 PROXY * * */
//connect to the SOCKS4 proxy server
write_log_info(
proxy, PROXYSOCKET_LOG_INFO,
"Preparing to connect to SOCKS4 proxy: %s:%lu", inet_ntoa(*(struct in_addr*)&proxyaddr), (unsigned long)proxyinfo->proxyport
);
if ((sock = proxyinfo_connect(proxy, proxyinfo->next, proxyinfo->proxyhost, proxyinfo->proxyport, errmsg)) == INVALID_SOCKET)
//ERROR_DISCONNECT_AND_ABORT("Error connecting to SOCKS4 proxy: %s:%lu", proxyinfo->proxyhost, (unsigned long)proxyinfo->proxyport);/////
ERROR_DISCONNECT_AND_ABORT(nullptr)
write_log_info(
proxy, PROXYSOCKET_LOG_INFO,
"Connected to SOCKS4 proxy: %s:%lu", inet_ntoa(*(struct in_addr*)&proxyaddr), (unsigned long)proxyinfo->proxyport
);
//send connect command
struct socks4_connect_request* request;
size_t requestlen = sizeof(struct socks4_connect_request);
if ((request = (struct socks4_connect_request*)malloc(requestlen)) == nullptr)
ERROR_DISCONNECT_AND_ABORT(memory_allocation_error)
request->socks_version = SOCKS4_VERSION;
request->socks_command = SOCKS4_COMMAND_CONNECT;
request->dst_port = htons(dstport);
request->dst_addr = (proxy->proxy_dns == USE_CLIENT_DNS ? hostaddr : htonl(0x000000FF));
request->userid[0] = 0;
if (proxyinfo->proxyuser && *proxyinfo->proxyuser)
{
requestlen += strlen(proxyinfo->proxyuser);
if ((request = (struct socks4_connect_request*)realloc(request, requestlen)) == nullptr)
ERROR_DISCONNECT_AND_ABORT(memory_allocation_error)
strcpy((char*)&(request->userid), proxyinfo->proxyuser);
}
if (proxy->proxy_dns)
{
size_t dsthostlen = (dsthost ? strlen(dsthost) : 0);
if ((request = (struct socks4_connect_request*)realloc(request, requestlen + dsthostlen + 1)) == nullptr)
ERROR_DISCONNECT_AND_ABORT(memory_allocation_error)
strcpy(((char*)request) + requestlen, (dsthost ? dsthost : ""));
requestlen += dsthostlen + 1;
}
if (!(proxyinfo->
proxyuser && *proxyinfo
->proxyuser))
write_log_info(
proxy, PROXYSOCKET_LOG_INFO,
"Connecting to destination: %s:%lu", (proxy->proxy_dns == USE_CLIENT_DNS ? inet_ntoa(*(struct in_addr*)&hostaddr) : dsthost),
(unsigned long)dstport
);
else
write_log_info(
proxy, PROXYSOCKET_LOG_INFO,
"Connecting to destination: %s:%lu (user-id: %s)",
(proxy->proxy_dns == USE_CLIENT_DNS ? inet_ntoa(*(struct in_addr*)&hostaddr) : dsthost), (unsigned long)dstport,
proxyinfo->proxyuser
);
if (send(sock, (void*)request, requestlen, 0) < requestlen)
ERROR_DISCONNECT_AND_ABORT("Error sending connect command to SOCKS4 proxy")
free(request);
//receive response
struct socks4_connect_request response{ };
if (recv(sock, (void*)&response, 8, 0) < 8)
ERROR_DISCONNECT_AND_ABORT("Connection lost while reading connect response from SOCKS4 proxy")
//display response information
if (response.socks_version != 0)
write_log_info(
proxy, PROXYSOCKET_LOG_WARNING,
"Invalid SOCKS4 reply code version (%u)", (unsigned int)response.socks_version
);
switch (response.socks_command)
{
case SOCKS4_STATUS_SUCCESS :
write_log_info(
proxy, PROXYSOCKET_LOG_INFO,
"SOCKS4 proxy connection established to: %s:%lu", inet_ntoa(*(struct in_addr*)&hostaddr), (unsigned long)dstport
);
break;
case SOCKS4_STATUS_FAILED :
ERROR_DISCONNECT_AND_ABORT("SOCKS4 connection rejected or failed")
case SOCKS4_STATUS_IDENT_FAILED :
ERROR_DISCONNECT_AND_ABORT("SOCKS4 request rejected because SOCKS server cannot connect to identd on the client")
case SOCKS4_STATUS_IDENT_MISMATCH :
ERROR_DISCONNECT_AND_ABORT("SOCKS4 request rejected because the client program and identd report different user-ids")
default :
ERROR_DISCONNECT_AND_ABORT("Unsupported reply from SOCKS4 server (%u)", (unsigned int)response.socks_command)
}
}
else if (proxyinfo->proxytype == PROXYSOCKET_TYPE_SOCKS5)
{
/* * * CONNECTION USING SOCKS5 PROXY * * */
//connect to the SOCKS5 proxy server
write_log_info(
proxy, PROXYSOCKET_LOG_INFO,
"Preparing to connect to SOCKS5 proxy: %s:%lu", proxyinfo->proxyhost, (unsigned long)proxyinfo->proxyport
);
if ((sock = proxyinfo_connect(proxy, proxyinfo->next, proxyinfo->proxyhost, proxyinfo->proxyport, errmsg)) == INVALID_SOCKET)
//ERROR_DISCONNECT_AND_ABORT("Error connecting to SOCKS5 proxy: %s:%lu", proxyinfo->proxyhost, (unsigned long)proxyinfo->proxyport);/////
ERROR_DISCONNECT_AND_ABORT(nullptr)
write_log_info(
proxy, PROXYSOCKET_LOG_INFO,
"Connected to SOCKS5 proxy: %s:%lu", proxyinfo->proxyhost, (unsigned long)proxyinfo->proxyport
);
//send initial data
if (!(proxyinfo->proxyuser && *proxyinfo->proxyuser) && !(proxyinfo->proxypass && *proxyinfo->proxypass))
{
uint8_t initsend[] = {SOCKS5_VERSION, 1, SOCKS5_METHOD_NOAUTH};
if (send(sock, (void*)initsend, sizeof(initsend), 0) < sizeof(initsend))
ERROR_DISCONNECT_AND_ABORT("Error sending data to SOCKS5 proxy")
}
else
{
uint8_t initsend[] = {SOCKS5_VERSION, 2, SOCKS5_METHOD_LOGIN, SOCKS5_METHOD_NOAUTH};
if (send(sock, (void*)initsend, sizeof(initsend), 0) < sizeof(initsend))
ERROR_DISCONNECT_AND_ABORT("Error sending data to SOCKS5 proxy")
}
//receive initial data
uint8_t initrecv[2];
if (recv(sock, (void*)initrecv, sizeof(initrecv), 0) < sizeof(initrecv))
ERROR_DISCONNECT_AND_ABORT("Connection lost while reading data from SOCKS5 proxy")
//display response information
if (initrecv[0] != SOCKS5_VERSION)
write_log_info(
proxy, PROXYSOCKET_LOG_WARNING,
"SOCKS5 proxy version mismatch (%u)", (unsigned int)initrecv[0]
);
static const char* methodmsg = "SOCKS5 proxy authentication method: %s";
switch (initrecv[1])
{
case SOCKS5_METHOD_NOAUTH :
write_log_info(
proxy, PROXYSOCKET_LOG_INFO, methodmsg,
"no authentication required"
);
break;
/*
* case SOCKS5_METHOD_GSSAPI :
* write_log_info(proxy, PROXYSOCKET_LOG_INFO, methodmsg, "GSSAPI");
* break;
*/
case SOCKS5_METHOD_LOGIN :
write_log_info(
proxy, PROXYSOCKET_LOG_INFO, methodmsg,
"username/password"
);
break;
case SOCKS5_METHOD_NONE :
write_log_info(
proxy, PROXYSOCKET_LOG_ERROR, methodmsg,
"no compatible methods"
);
ERROR_DISCONNECT_AND_ABORT("Unable to negociate SOCKS5 proxy authentication method")
default :
write_log_info(
proxy, PROXYSOCKET_LOG_ERROR, methodmsg,
"unknown (%u)", (unsigned int)initrecv[1]
);
ERROR_DISCONNECT_AND_ABORT("Received unknown SOCKS5 proxy authentication method (%u)", (unsigned int)initrecv[1])
}
//authenticate if needed
if (initrecv[1] == SOCKS5_METHOD_LOGIN)
{
char* authbuf;
size_t proxyuserlen = (proxyinfo->proxyuser ? strlen(proxyinfo->proxyuser) : 0);
size_t proxypasslen = (proxyinfo->proxypass ? strlen(proxyinfo->proxypass) : 0);
size_t authbuflen = 3 + proxyuserlen + proxypasslen;
if ((authbuf = (char*)malloc(authbuflen)) == nullptr)
ERROR_DISCONNECT_AND_ABORT(memory_allocation_error)
authbuf[0] = 1;
authbuf[1] = proxyuserlen;
memcpy(authbuf + 2, proxyinfo->proxyuser, proxyuserlen);
authbuf[2 + proxyuserlen] = proxypasslen;
memcpy(authbuf + 3 + proxyuserlen, proxyinfo->proxypass, proxypasslen);
write_log_info(
proxy, PROXYSOCKET_LOG_INFO,
"Sending authentication (login: %s)", (proxyinfo->proxyuser ? proxyinfo->proxyuser : nullptr));
if (send(sock, (void*)authbuf, authbuflen, 0) < authbuflen)
{
free(authbuf);
ERROR_DISCONNECT_AND_ABORT("Error sending authentication data to SOCKS5 proxy")
}
free(authbuf);
if (recv(sock, (void*)initrecv, sizeof(initrecv), 0) < sizeof(initrecv))
ERROR_DISCONNECT_AND_ABORT("Connection lost while reading authentication response from SOCKS5 proxy")
if (initrecv[0] != 1)
ERROR_DISCONNECT_AND_ABORT("SOCKS5 proxy subnegotiation version mismatch (%u)", (unsigned int)initrecv[0])
if (initrecv[1] == SOCKS5_STATUS_CONNECTION_REFUSED)
ERROR_DISCONNECT_AND_ABORT("SOCKS5 access denied")
if (initrecv[1] != 0)
ERROR_DISCONNECT_AND_ABORT("SOCKS5 authentication failed with status code %u (login: %s)", (unsigned int)initrecv[1],
(proxyinfo->proxyuser ? proxyinfo->proxyuser : nullptr))
/////TO DO: check and report status
//write_log_info(proxy, PROXYSOCKET_LOG_INFO, "SOCKS5 authentication succeeded (login: %s)", (proxyinfo->proxyuser ? proxyinfo->proxyuser : NULL));
}
//send connect command
if (proxy->proxy_dns == USE_CLIENT_DNS)
{
struct socks5_connect_request_ipv4
request = {SOCKS5_VERSION, SOCKS5_COMMAND_CONNECT, 0, SOCKS5_ADDRESSTYPE_IPV4, hostaddr, htons(dstport)};
write_log_info(
proxy, PROXYSOCKET_LOG_INFO,
"Connecting to IPv4 destination: %s:%lu", inet_ntoa(*(struct in_addr*)&hostaddr), (unsigned long)dstport
);
if (send(sock, (void*)&request, sizeof(request), 0) < sizeof(request))
ERROR_DISCONNECT_AND_ABORT("Error sending connect command with IPv4 address to SOCKS5 proxy")
}
else
{
struct socks5_connect_request_ipv4* request;
size_t dsthostlen = (dsthost ? strlen(dsthost) : 0);
int requestlen = sizeof(struct socks5_connect_request_ipv4) - sizeof(uint32_t) + 1 + dsthostlen;
request = (struct socks5_connect_request_ipv4*)malloc(requestlen);
request->socks_version = SOCKS5_VERSION;
request->socks_command = SOCKS5_COMMAND_CONNECT;
request->reserved = 0;
request->socks_addresstype = SOCKS5_ADDRESSTYPE_DOMAINNAME;
*(uint8_t*)&(request->dst_addr) = dsthostlen;
if (dsthostlen > 0)
memcpy((void*)(((uint8_t*)&(request->dst_addr)) + 1), dsthost, dsthostlen);
*(uint16_t*)(((uint8_t*)&(request->dst_addr)) + 1 + dsthostlen) =
htons(dstport);