Есть программа (самодельная), которая должна (по идее) запускаться вместе с системой (под правами рута) и работать до её выключения без вмешательства.
Программа работает как "тупая" http-прокся, которая понимает один запрос - "CONNECT", отлавливает его, изменяет адрес сервера назначения и пересылает запрос на другую проксю,
после чего открывает и поддерживает туннель.
Проблема в том, что несколько раз в неделю она вываливается с Segmentation Fault, из-за чего её приходится перезапускать вручную.
Вопрос: как можно найти глюк? Программа многопоточная, иначе можно было бы запустить её при помощи gdb.
Какие ещё есть варианты? Как отловить адрес ошибки в самой программе?
Буду признателен за линки/"указатели направления"/RTFM с указанием FM и т.д.
Исходники ниже.
Написана через месяц после перехода на систему, изначально портирована с Win/MSVC2005, из-за этого
в кода местами бардак.:
main.cpp:
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.
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>
#include <netdb.h>
//#include <winsock2.h>
#include <netinet/in.h>
#include <memory.h>
#include <arpa/inet.h>
#include "buffer.h"
#include <errno.h>
#include <pthread.h>
#include <signal.h>
typedef unsigned long DWORD;
//#pragma comment(lib, "Ws2_32.lib")
SOCKET input = INVALID_SOCKET;
int portIndex = 8100 ;
bool running = true;
const char* proxyAddr = "80.82.32.27";//"cache.vsi.ru";
const int proxyPort = 3128 ;
/*const char* proxyAddr = "128.0.0.1";
const int proxyPort = 8080;*/
inline bool strCmp(const char* str1, const char* str2){
if (!str1||!str2) return false;
while ((*str1)&&(*str2)){
if ((*(str1++))!=(*(str2++)))
return false;
}
if (*str1 != *str2) return false;
return true;
}
void sigHandler (int sigNum){
//fprintf(stderr, "shutting
running = false;
}
SOCKET bindPort(int port){
SOCKET result;
struct sockaddr_in addr;
if((result = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET){
fprintf(stderr, "Create socket failed.\n");
return INVALID_SOCKET;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if(bind(result, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR){
fprintf(stderr, "bind() failed.\nreason: %s\n\n", strerror(errno));
return INVALID_SOCKET;
}
if(listen(result, 1) == SOCKET_ERROR){
fprintf(stderr, "listen() failed.\nreason: %s\n\n", strerror(errno));
return INVALID_SOCKET;
}
return result;
}
struct TunnelData{
SOCKET src;
SOCKET dest;
TunnelData(void){
src = dest = INVALID_SOCKET;
}
TunnelData(const SOCKET _src, const SOCKET _dest)
:src(_src), dest(_dest){
}
};
//DWORD WINAPI TunnelThread(void* p){
void* tunnelThread(void* p){
TunnelData* tunnelData = (TunnelData*)p;
SOCKET src = tunnelData->src;
SOCKET dest = tunnelData->dest;
char tmp[4096];
while(true){
//int size = recv(src, tmp, sizeof(tmp), 0);
int size = read(src, tmp, sizeof(tmp));
if (size == 0){
write(dest, 0, 0);
//send(dest, 0, 0, 0);
break;//connection closed;
}
if (size < 0)
break;
/*if (size == SOCKET_ERROR){
if (WSAGetLastError() == WSAECONNRESET)
continue;
break;
}*/
int offset = 0;
while(offset < size){
//int sent = send(dest, &tmp[offset], size - offset, 0);
int sent = write(dest, &tmp[offset], size - offset);
if ((sent == SOCKET_ERROR)||(sent == 0))
break;
offset += sent;
}
if (offset < size)
break;
}
shutdown(src, SHUT_RD);//SD_RECEIVE);
shutdown(dest, SHUT_WR);//SD_SEND);
// delete tunnelData;
return 0;
}
struct ConnectionData{
SOCKET socket;
sockaddr_in srcAddr;
sockaddr_in destAddr;
ConnectionData(const SOCKET _socket, const sockaddr_in& _srcAddr, const sockaddr_in& _destAddr){
socket = _socket;
srcAddr = _srcAddr;
destAddr = _destAddr;
}
};
void printAddress(const sockaddr_in& addr){
union{
DWORD dw;
unsigned char c[4];
} from;
from.dw = ntohl(addr.sin_addr.s_addr);
fprintf(stderr, "access from %d.%d.%d.%d\n", from.c[3], from.c[2], from.c[1], from.c[0]);
}
struct Filter{
const char* oldStr;
const char* newStr;
};
const Filter filters[] = {
{"CONNECT vir1a.toonel.net:8081 HTTP/1.1\r\n\r\n", "CONNECT vir1a.toonel.net:443 HTTP/1.1\r\n\r\n"},
{"CONNECT dub1.toonel.net:8081 HTTP/1.1\r\n\r\n", "CONNECT vir1a.toonel.net:443 HTTP/1.1\r\n\r\n"},
{"CONNECT vir1b.toonel.net:8081 HTTP/1.1\r\n\r\n", "CONNECT vir1a.toonel.net:443 HTTP/1.1\r\n\r\n"},
{"CONNECT vir1a.toonel.net:8081 HTTP/1.1\n\n", "CONNECT vir1a.toonel.net:443 HTTP/1.1\r\n\r\n"},
{"CONNECT dub1.toonel.net:8081 HTTP/1.1\n\n", "CONNECT vir1a.toonel.net:443 HTTP/1.1\r\n\r\n"},
{"CONNECT vir1b.toonel.net:8081 HTTP/1.1\n\n", "CONNECT vir1a.toonel.net:443 HTTP/1.1\r\n\r\n"},
{0, 0}
};
void processConnection(const SOCKET srcSock, const sockaddr_in& srcAddr, const sockaddr_in& destAddr){
printAddress(srcAddr);
fprintf(stderr, "creating proxy socket\n");
SOCKET destSock = socket(AF_INET, SOCK_STREAM, 0);
if (destSock == INVALID_SOCKET){
fprintf(stderr, "can't create proxy socket!\n");
shutdown(srcSock, SHUT_RD);//SD_BOTH);
//closesocket(srcSock);
close(srcSock);
return;
}
fprintf(stderr, "connecting dest socket\n");
if (connect(destSock, (const sockaddr*)&destAddr, sizeof(destAddr)) != 0){
fprintf(stderr, "can't connect dest proxy!\n");
shutdown(srcSock, SHUT_RDWR);//SD_BOTH);
//closesocket(srcSock);
//closesocket(destSock);
return;
}
fprintf(stderr, "reading http data\n");
Buffer buf;
buf.appendHttp(srcSock);
buf.append('\0');
for (const Filter* filter = filters; filter->oldStr; filter++){
if (strCmp(filter->oldStr, buf.getData())==0){
buf.clear();
buf.append(filter->newStr);
buf.append('\0');
fprintf(stderr, "filter found\n");
break;
}
}
fprintf(stderr, "client said:\n%s\nretrasmitting to proxy\n", buf.getData());
//send(destSock, buf.getData(), buf.getSize()-1, 0);
write(destSock, buf.getData(), buf.getSize()-1);
buf.clear();
buf.appendHttp(destSock);
buf.append('\0');
fprintf(stderr, "proxy answered:\n%s\nretrasmitting to client\n", buf.getData());
write(srcSock, buf.getData(), buf.getSize()-1);
//send(srcSock, buf.getData(), buf.getSize()-1, 0);
fprintf(stderr, "starting tunnels\n");
/*HANDLE threads[2];
threads[0] = CreateThread(0, 0, TunnelThread, new TunnelData(srcSock, destSock), 0, 0);
threads[1] = CreateThread(0, 0, TunnelThread, new TunnelData(destSock, srcSock), 0, 0);
WaitForMultipleObjects(2, threads, TRUE, INFINITE);
CloseHandle(threads[0]);
CloseHandle(threads[1]);*/
pthread_t threads[2];
TunnelData tunnelData[2];
tunnelData[0].src = srcSock;
tunnelData[0].dest = destSock;
tunnelData[1].src = destSock;
tunnelData[1].dest = srcSock;
pthread_create(&threads[0], 0, tunnelThread, &tunnelData[0]);
pthread_create(&threads[1], 0, tunnelThread, &tunnelData[1]);
pthread_join(threads[0], 0);
pthread_join(threads[1], 0);
//closesocket(destSock);
close(destSock);
//closesocket(srcSock);
close(srcSock);
}
void* /*DWORD WINAPI*/ threadProc(void* p){
ConnectionData* connectionData = (ConnectionData*)p;
processConnection(connectionData->socket, connectionData->srcAddr, connectionData->destAddr);
delete connectionData;
//return 0;
}
void mainLoop(const sockaddr_in& proxyAddr){
fprintf(stderr, "main loop started\n");
while (running){
sockaddr_in srcAddr;
//int len = sizeof(srcAddr);
socklen_t len = sizeof(srcAddr);
SOCKET incoming = accept(input, (sockaddr*)&srcAddr, &len);
if (incoming == INVALID_SOCKET){
//int wsaError = WSAGetLastError();
//if (wsaError == WSAEINTR)
int errorNum = errno;
if (errorNum == EINTR)
continue;
fprintf(stderr, "Cannot accept request, error index: %d\n", errorNum);//wsaError);
break;
}
fprintf(stderr, "new connection.\n");
ConnectionData* connectionData = new ConnectionData(incoming, srcAddr, proxyAddr);
//DWORD threadId;
//HANDLE thread = CreateThread(0, 0, threadProc, (void*)connectionData, 0, &threadId);
//CloseHandle(thread);
pthread_t thread;
pthread_create(&thread, 0, threadProc, (void*)connectionData);//TODO very dangerous
//threadProc(connectionData);
}
fprintf(stderr, "main loop completed\n");
}
/*BOOL WINAPI ctrlHandler(DWORD ctrlType){
fprintf("shutting down the app\n");
running = false;
closesocket(input);
input = INVALID_SOCKET;
return TRUE;
}*/
bool isAddr(const char* p){
for (;*p; p++){
char c = *p;
if (isdigit(c) == 0)
if (c != '.')
return false;
}
return true;
}
bool getProxyAddr(sockaddr_in& saddr, const char* addr, const int port){
hostent* host;
if (isAddr(addr)){
unsigned long inetAddr = inet_addr(addr);
host = gethostbyaddr((char*)&inetAddr, 4, AF_INET);
}
else
host = gethostbyname(addr);
if (!host) return false;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
memcpy(&saddr.sin_addr.s_addr, host->h_addr, host->h_length);
/*saddr.sin_addr.S_un.S_un_b.s_b1 = host->h_addr[0];
saddr.sin_addr.S_un.S_un_b.s_b2 = host->h_addr[1];
saddr.sin_addr.S_un.S_un_b.s_b3 = host->h_addr[2];
saddr.sin_addr.S_un.S_un_b.s_b4 = host->h_addr[3];*/
return true;
}
int main(void){
fprintf(stderr, "this is a simple windows redirector\n");
//WSADATA wsaData;
//WSAStartup(MAKEWORD(2, 2), &wsaData);
sockaddr_in proxySAddr;
if (!getProxyAddr(proxySAddr, proxyAddr, proxyPort)){
fprintf(stderr, "getProxyAddr(\"%s\") failed.\n", proxyAddr);
//WSACleanup();
return 0;
}
input = bindPort(portIndex);
//SetConsoleCtrlHandler(ctrlHandler, true);
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sigHandler;
sigaction(SIGINT, &sa, 0);
sigaction(SIGTERM, &sa, 0);
if (input != INVALID_SOCKET){
mainLoop(proxySAddr);
if (input != INVALID_SOCKET)
close/*socket*/(input);
fprintf(stderr, "closing down app...\n");
}
else
fprintf(stderr, "an error has occured\n");
//WSACleanup();
return 0 ;
}
buffer.h
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.
//#pragma once
#ifndef _BUFFER_H_
#define _BUFFER_H_
typedef unsigned long dword;
typedef int SOCKET;
#define SOCKET_ERROR - 1
#define INVALID_SOCKET - 1
class Buffer{
protected:
char* data;
dword size;
dword capacity;
dword increment;
public:
inline dword getSize(void){return size;}
inline const char* getData(void) const {return data;}
void clear(void);
void grow(void);
void append(const char* src, dword size);
void append(const char* src);
void append(const char c);
bool appendHttp(SOCKET src);//true if succesful
Buffer(dword capacity = 0x1000, dword increment = 0 );
~Buffer(void);
};
#endif //_BUFFER_H_
buffer.cpp
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.
//#include <winsock2.h>
//#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "buffer.h"
#include <memory.h>
#include <errno.h>
//#include <string.h>
void Buffer::grow(void){
dword newCapacity = increment ? capacity + increment: capacity * 2 ;
char* newData = new char[newCapacity];
memcpy(newData, data, size);
delete[] data;
data = newData;
}
Buffer::Buffer(dword _capacity, dword _increment)
:size( 0 ), capacity(_capacity), increment(_increment), data( 0 ){
data = new char[capacity];
}
void Buffer::clear(void){
size = 0 ;
}
Buffer::~Buffer(void){
delete[] data;
}
void Buffer::append(const char* src, dword srcsize){
while (size + srcsize > capacity)
grow();//TODO НОЮЯМН.
memcpy(&data[size], src, srcsize);
size += srcsize;
}
void Buffer::append(const char* src){
dword size = (dword)strlen(src);
append(src, size);
}
void Buffer::append(const char c){
append(&c, 1 );
}
bool Buffer::appendHttp(SOCKET src){
int startSize = size;
for (int dataSize = 0 ; true;){
char tmp[ 4096 ];
if ((size-startSize) >= 4 )
if (memcmp(&data[size- 4 ], "\r\n\r\n", 4 )== 0 )
break;
if ((size-startSize) >= 2 )
//if (*((dword*)&data[size-4]) == 0x0a0d0a0d)
if (memcmp(&data[size- 2 ], "\n\n", 2 )== 0 )
break;
//dataSize = read(src, tmp, sizeof(tmp));
dataSize = recv(src, tmp, sizeof(tmp), 0 );
if (dataSize == 0 )
break;
if (dataSize == SOCKET_ERROR)
return false;
/*if (WSAGetLastError() == WSAECONNRESET)
if (errno == ECONNRESET
continue;
else{
//TODO: size = startSize?
return false;
}*/
append(tmp, dataSize);
}
return true;
}
Makefile:
1. 2. 3. 4. 5. 6. 7.
CXXFLAGS=-O2
redirector: main.o buffer.o
g++ -o redirector -g main.o buffer.o -lpthread
clean:
rm -f main.o buffer.o redirector
Буду рад выслушать любые замечания по коду.
Posted via ActualForum NNTP Server 1.4
|