2.char* strsub (char *instr, unsigned start, unsigned end)
3.{
4. unsigned n = end - start;
5. char * outstr = (char *)malloc(n+1);
6. //bzero(outstr,n+1);
7. strncpy (outstr, instr + start, n);
8. outstr[n] = 0;
9. return outstr;
10.}
11.
12.int setnonblocking(int sockfd)
13.{
14. if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1)
15. {
16. return -1;
17. }
18. return 0;
19.}
20.
21.void smtp_echo(void* data)
22.{
23. int socket = *(int*)data;
24. char ebuf[128],buffer[BUFFER_SIZE];
25. int length = 0, z;
26. regex_t reg;
27. regmatch_t pm[10];
28. const size_t nmatch = 10;
29. const char * split = "\r\n";
30. char * pline, * cmd;
31.
32. z = regcomp (®, smtp_cmd_format, REG_EXTENDED);
33. if (z != 0){
34. regerror (z,®, ebuf, sizeof (ebuf));
35. fprintf (stderr, "%s: regcomp()\n", ebuf);
36. return;
37. }
38.
39. {
40. while (1) {
41. bzero(buffer,BUFFER_SIZE);
42. length = recv(socket,buffer,BUFFER_SIZE,0);
43. if (length == -1) {
44. if(errno == EAGAIN){
45. break;
46. }
47. syslog(LOG_ERR,"recv - %m");
48. break;
49. }
50. syslog(LOG_DEBUG,"%s",buffer);
51.
52. pline = strtok (buffer,split);
53. while(pline!=NULL) {
54. syslog(LOG_DEBUG,"%s\n",pline);
55. if (0==(strcasecmp(pline, "."))){
56. smtp_cmd("HELO");
57. continue;
58. }
59. z = regexec (®, pline, nmatch, pm, 0);
60. if (z == REG_NOMATCH)
61. {
62. // do nothing;
63. }
64. else if (z != 0)
65. {
66. regerror (z,®, ebuf, sizeof (ebuf));
67. fprintf (stderr, "%s: regexec('%s')\n", ebuf, pline);
68. return ;
69. }
70.
71. if(pm[1].rm_so != -1)
72. {
73. cmd = strsub (pline, pm[1].rm_so, pm[1].rm_eo);
74. syslog(LOG_NOTICE,"cmd => %s\n", cmd);
75. if(pm[2].rm_so != -1)
76. {
77. syslog(LOG_NOTICE,"other content => %s\n", strsub (pline, pm[2].rm_so, pm[2].rm_eo));
78. }
79.
80. smtp_cmd(cmd,socket);
81. }
82. pline = strtok(NULL,split);
83. }
84.
85. if(length < BUFFER_SIZE)
86. break;
87. }
88.
89. }
90.
91. regfree (®);
92. return;
93.}
94.
95.void smtp_cmd(char * cmd,int socket)
96.{
97. char buffer[BUFFER_SIZE];
98. bzero(buffer, BUFFER_SIZE);
99.
100. if(0 == (strcasecmp(cmd,"HELO")))
101. {
102. strcpy(buffer,"250 Regards from CharlesCui\r\n");
103. send(socket,buffer,strlen(buffer),0);
104. } else if(0==(strcasecmp(cmd,"QUIT")))
105. {
106. strcpy(buffer,"221 QUIT OK\r\n");
107. send(socket,buffer,strlen(buffer),0);
108. close(socket);
109. epoll_ctl(kdpfd, EPOLL_CTL_DEL, socket, &ev);
110. } else if(0==(strcasecmp(cmd,"NOOP")))
111. {
112. strcpy(buffer,"250 NOOP\r\n");
113. send(socket,buffer,strlen(buffer),0);
114. } else if(0==(strcasecmp(cmd,"DATA")))
115. {
116. strcpy(buffer,"354 End data with <CR><LF>.<CR><LF>\r\n");
117. send(socket,buffer,strlen(buffer),0);
118. } else if(0==(strcasecmp(cmd,"EHLO")))
119. {
120. strcpy(buffer,"334 250-mail\r\n250-PIPELINING\r\n250-AUTH LOGIN PLAIN\r\n250-AUTH=LOGIN PLAIN\r\n250 8BITMI\r\n");
121. send(socket,buffer,strlen(buffer),0);
122. } else if(0==(strcasecmp(cmd,"AUTH")))
123. {
124. strcpy(buffer,"334 dXNlcm5hbWU6\r\n");
125. send(socket,buffer,strlen(buffer),0);
126. } else if(0==(strcasecmp(cmd, "MAIL")))
127. {
128. strcpy(buffer,"250 Mail Ok\r\n");
129. send(socket,buffer,strlen(buffer),0);
130. } else if(0==(strcasecmp(cmd, "RCPT")))
131. {
132. strcpy(buffer,"250 Rcpt Ok\r\n");
133. send(socket,buffer,strlen(buffer),0);
134. } else if(0==(strcasecmp(cmd,"220")))
135. {
136. strcpy(buffer,"220 Welcome to CharlesCui's smtpd mock server.\r\n");
137. send(socket,buffer,strlen(buffer),0);
138. } else
139. {
140. strcpy(buffer,"");
141. send(socket,buffer,strlen(buffer),0);
142. syslog(LOG_NOTICE,"Error smtp command.");
143. }
144.}
145.
146.int init_smtp(int port)
147.{
148. struct sockaddr_in *server_addr;
149. server_addr = malloc(sizeof(struct sockaddr_in));
150. server_addr->sin_family = AF_INET;
151. server_addr->sin_addr.s_addr = htons(INADDR_ANY);
152. server_addr->sin_port = htons(port);
153.
154. int server_socket = socket(AF_INET,SOCK_STREAM,0);
155. syslog(LOG_NOTICE,"init_smtp:server_socket => %d\n",server_socket);
156. setnonblocking(server_socket);
157. if( server_socket < 0)
158. {
159. syslog(LOG_ERR,"Create Socket Failed! - %m\n");
160. exit(1);
161. }
162.
163. if( bind(server_socket,(struct sockaddr*)server_addr,sizeof(struct sockaddr_in)))
164. {
165. syslog(LOG_ERR,"Server Bind Port : %d Failed! - %m\n", port);
166. exit(1);
167. }
168.
169. if ( listen(server_socket, g_listen_size) )
170. {
171. syslog(LOG_ERR,"Server Listen Failed! - %m\n");
172. exit(1);
173. }
174.
175.
176. struct rlimit rt;
177. rt.rlim_max = rt.rlim_cur = g_epoll_size;
178. if (setrlimit(RLIMIT_NOFILE, &rt) == -1)
179. {
180. syslog(LOG_ERR,"setrlimit - %m");
181. exit(1);
182. }
183. else
184. {
185. syslog(LOG_NOTICE,"设置系统资源参数成功!\n");
186. }
187.
188. return server_socket;
189.}
190.void block_queue(void * param)
191.{
192. /*
193. 姑娘们,排好对,等客了!
194. 老鸨吩咐要做什么都知道了吗?(func为回调函数)
195. */
196. void(* func)(void* );
197. int fd;
198. block_queue_node_t *head_node;
199.
200. //param是全局变量bqp
201. block_queue_param_t* bque = (block_queue_param_t*)param;
202. func = bque->func;
203.
204. for(;;)
205. {
206. pthread_mutex_lock(&bque->mutex);
207. pthread_cond_wait(&bque->cond,&bque->mutex);
208. /*
209. 来客啦!
210. */
211. if(list_empty(&head))
212. {
213. //哪个小二瞎喊,命名一个客人都没来!
214. pthread_mutex_unlock(&bque->mutex);
215. continue;
216. }else
217. {
218. /*
219. 大爷,跟我走吧,我那儿宽敞
220. 从链表头部取出一个节点
221. */
222. head_node = list_entry(head.next,block_queue_node_t,list);
223. fd = head_node->fd;
224. //大爷,你是我的了!
225. //同时删除该节点
226. list_del(&head_node->list);
227.
228. free(head_node);
229. counter--;
230. }
231.
232. pthread_mutex_unlock(&bque->mutex);
233.
234. /*xxoo(干)*/
235. func(&fd);
236. }
237.}
238.
239.int insert_queue(block_queue_param_t *bque,int fd)
240.{
241. //生成临时节点,用来保存fd
242. block_queue_node_t *b = (block_queue_node_t *)malloc(sizeof(block_queue_node_t));
243. b->fd = fd;
244.
245. pthread_mutex_lock(&bque->mutex);
246.
247. if(counter > g_listen_size){
248. //当客人数量超过小姐接待能力的时候
249. //就放弃接待该客人,并且返回1.
250. //青楼是残酷滴,一个萝卜一个坑
251. return 1;
252. }else{
253. counter++;
254. }
255. /*
256. 将新增的节点插入到尾部,
257. 相对应的,block_queue循环体中取节点时,
258. 是从链表头取到的.
259. */
260. list_add_tail(&b->list,&head);
261. /*
262. 客人到!
263. 姐妹们快抢客啊!(内核用broadcast通知各阻塞的线程)
264. */
265. pthread_cond_broadcast(&bque->cond);
266. pthread_mutex_unlock(&bque->mutex);
267.
268. return 0;
269.}
270.
271.int init_threads()
272.{
273. size_t i=0;
274. //这是今天的流水账,
275. //客人们来了都会在这里(head链表)登记的.
276. //都知道今天各位姑娘要做什么吧(smtp_echo).
277. //为全局变量bqp设置属性
278. bqp.func = (void*)smtp_echo;
279. /*
280. 不许抢客人!(互斥mutex)
281. 说了多少次了,不管男女老幼长短粗细,
282. 只有客人想不到,没有我们做不到!
283. 别只盯着帅哥.
284. */
285. pthread_cond_init(&bqp.cond,NULL);
286. pthread_mutex_init(&bqp.mutex,NULL);
287.
288. /*
289. 姑娘们起床了!
290. 初始化各个线程
291. */
292. for( i = 0; i < g_th_count; ++i)
293. {
294. pthread_t child_thread;
295. pthread_attr_t child_thread_attr;
296. pthread_attr_init(&child_thread_attr);
297. pthread_attr_setdetachstate(&child_thread_attr,PTHREAD_CREATE_DETACHED);
298. /*
299. 养你们是要干活(block_queue)的,
300. 没活的时候可以休息着(pthread_cond_wait)
301. 活来了(pthread_cond_signal)就麻利点去接客(head链表非空)
302. */
303. if( pthread_create(&child_thread,&child_thread_attr,(void *)block_queue, (void *)&bqp) < 0 )
304. {
305. syslog(LOG_ERR,"pthread_create Failed : %s - %m\n",strerror(errno));
306. return 1;
307. }
308. else
309. {
310. syslog(LOG_NOTICE,"pthread_create Success : %d\n",(int)child_thread);
311. }
312. }
313.
314.}
315.
316.int handler(void* fd)
317.{
318. syslog(LOG_NOTICE,"handler:fd => %d\n",*(int *)(fd));
319. //向全局变量bqp中插入一个节点
320. //姑娘们听好了,
321. //大爷都在排队呢,
322. //一个个麻利点,伺候起来了!
323. return insert_queue(&bqp,*(int *)fd);
324.}
325.
326.void init_daemon(void)
327.{
328. int pid;
329. int i;
330. if(pid=fork())
331. exit(0);//是父进程,结束父进程
332. else if(pid< 0)
333. exit(1);//fork失败,退出
334. //是第一子进程,后台继续执行
335. setsid();//第一子进程成为新的会话组长和进程组长
336. //并与控制终端分离
337. if(pid=fork())
338. exit(0);//是第一子进程,结束第一子进程
339. else if(pid< 0)
340. exit(1);//fork失败,退出
341. //是第二子进程,继续
342. //第二子进程不再是会话组长
343.
344. for(i=0;i< NOFILE;++i)//关闭打开的文件描述符
345. close(i);
346. chdir("/tmp");//改变工作目录到/tmp
347. umask(0);//重设文件创建掩模
348. return;
349.}
350.
351.int main(int argc, char **argv)
352.{
353. char ch;
354. int d = 0;
355.
356. //处理argv
357. while( ( ch = getopt( argc, argv, "p:t:l:e:d?" ) ) != EOF )
358. {
359. switch(ch)
360. {
361. case 'p':
362. printf("SMTPD_PORT =>%s ", optarg);
363. g_port = atoi(optarg);
364. break;
365. case 't':
366. printf("THREADS_COUNT => %s ", optarg);
367. g_th_count = atoi(optarg);
368. break;
369. case 'l':
370. printf("LENGTH_OF_LISTEN_QUEUE => %s. ",optarg);
371. g_listen_size = atol(optarg);
372. break;
373. case 'e':
374. printf("MAX_EPOLL_SIZE => %s. ",optarg);
375. g_epoll_size = atol(optarg);
376. break;
377. case 'd':
378. printf("RUN AS DAEMON. ");
379. d = 1;
380. case '?':
381. printf("Useage: -p [SMTPD_PORT|8025] -t [THREADS_COUNT|100] -l [LENGTH_OF_LISTEN_QUEUE|1024] -e [MAX_EPOLL_SIZE|1000] -d (RUN AS DAEMON.)\n");
382. exit(1);
383. default:
384. printf("Not support option :%c\n",ch);
385. exit(2);
386. }
387. }
388.
389. if(d == 1)
390. init_daemon();
391.
392. //一天的流水账要记录下来啊
393. //初始化 syslog
394. char *ident = "Smtp Mock";
395. int logopt = LOG_PID | LOG_CONS;
396. int facility = LOG_USER;
397. openlog(ident, logopt, facility);
398. setlogmask(LOG_UPTO(LOG_ERR));
399.
400. syslog(LOG_INFO,"syslog inited.");
401.
402. //初始化链表
403. INIT_LIST_HEAD(&head);
404.
405. //生成smtp套接字
406. //本店开张了,欢迎访问
407. int server_socket = init_smtp(g_port);
408. int n;
409.
410. if(init_threads() == 0)
411. syslog(LOG_NOTICE,"Success full init_threads.");
412.
413. /*
414. 下面要把本店加入全球领先的企业管理系统中,
415. 该系统节省人力资源,
416. 不需要服务员傻等在门口,
417. 而是客人到了就会通知服务员出来迎宾.
418. */
419. kdpfd = epoll_create(g_epoll_size);
420. ev.events = EPOLLIN | EPOLLET;
421. ev.data.fd = server_socket;
422. if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, server_socket, &ev) < 0) {
423. fprintf(stderr, "epoll set insertion error: fd=%d < 0",
424. server_socket);
425. return -1;
426. }
427. //老鸨(主线程)负责拉客,姑娘(子线程)负责接客
428. for(;;) {
429. struct sockaddr_in local;
430. socklen_t length = sizeof(local);
431. int client;
432.
433. //epoll_wait实现了阻塞,而不是busy loop
434. nfds = epoll_wait(kdpfd, events, g_epoll_size, -1);
435.
436. for(n = 0; n < nfds; ++n) {
437. //判断套接字
438. //看是熟客还是生客
439. if(events[n].data.fd == server_socket) {
440. //新新新新,新来的吧
441. //你是新新新新新来的吧
442. client = accept(server_socket, (struct sockaddr *) &local,&length);
443. //是生客就发一个免费会员卡(client)
444. if(client < 0){
445. syslog(LOG_ERR,"accept - %m");
446. continue;
447. }
448. setnonblocking(client);
449. //先跟大爷打声招呼,显得我们姑娘主动些
450. smtp_cmd("220",client);
451. ev.events = EPOLLIN | EPOLLET;
452. ev.data.fd = client;
453. /*
454. 再发张VIP卡,
455. 把大爷加入VIP客户列表,
456. 享受天上人间的服务
457. */
458. if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {
459. fprintf(stderr, "epoll set insertion error: fd=%d < 0",
460. client);
461. return -1;
462. }
463. }
464. else
465. /*
466. 这位大爷肯定来过好几次了,
467. 否则怎么连后门都知道.
468. */
469. /*
470. 后屋一排姑娘,大爷您慢慢挑
471. 老鸨就不奉陪了,姑娘们伺候着!
472. */
473. if(handler((void *)&events[n].data.fd) != 0)
474. syslog(LOG_ERR,"handler ret != 0 - %m");
475. }
476. }
477. //打击色..情产业,
478. //被迫歇业了
479. close(server_socket);
480. return 0;
481.