Subversion Repositories Kolibri OS

Rev

Rev 3704 | Rev 3824 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3545 hidnplayr 1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2
;;                                                                 ;;
3618 hidnplayr 3
;; Copyright (C) KolibriOS team 2010-2013. All rights reserved.    ;;
3545 hidnplayr 4
;; Distributed under terms of the GNU General Public License       ;;
5
;;                                                                 ;;
6
;;  ftpd.asm - FTP Daemon for KolibriOS                            ;;
7
;;                                                                 ;;
8
;;  Written by hidnplayr@kolibrios.org                             ;;
9
;;                                                                 ;;
10
;;          GNU GENERAL PUBLIC LICENSE                             ;;
11
;;             Version 2, June 1991                                ;;
12
;;                                                                 ;;
13
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
14
 
15
DEBUG                   = 0             ; if set to one, program will run in a single thread
16
 
17
BUFFERSIZE              = 8192
18
 
19
; using multiple's of 4
20
STATE_CONNECTED         = 0*4
21
STATE_LOGIN             = 1*4
22
STATE_LOGIN_FAIL        = 2*4           ; When an invalid username was given
23
STATE_ACTIVE            = 3*4
24
 
25
TYPE_UNDEF              = 0
26
 
27
TYPE_ASCII              = 00000100b
28
TYPE_EBDIC              = 00001000b
29
; subtypes for ascii & ebdic (np = default)
30
TYPE_NP                 = 00000001b     ; non printable
31
TYPE_TELNET             = 00000010b
32
TYPE_ASA                = 00000011b
33
 
34
TYPE_IMAGE              = 01000000b     ; binary data
35
TYPE_LOCAL              = 10000000b     ; bits per byte must be specified
36
                                        ; lower 4 bits will hold this value
37
MODE_NOTREADY           = 0
38
MODE_ACTIVE             = 1
39
MODE_PASSIVE_WAIT       = 2
40
MODE_PASSIVE_OK         = 3
41
MODE_PASSIVE_FAILED     = 4
42
 
43
PERMISSION_EXEC         = 1b            ; LIST
44
PERMISSION_READ         = 10b
45
PERMISSION_WRITE        = 100b
46
PERMISSION_DELETE       = 1000b
47
PERMISSION_CD           = 10000b        ; Change Directory
48
 
49
ABORT                   = 1 shl 31
50
 
51
format binary as ""
52
 
53
use32
54
 
55
        org     0x0
56
 
57
        db      'MENUET01'      ; signature
58
        dd      1               ; header version
59
        dd      start           ; entry point
60
        dd      i_end           ; initialized size
61
        dd      mem+0x1000      ; required memory
62
        dd      mem+0x1000      ; stack pointer
63
        dd      params          ; parameters
64
        dd      path            ; path
65
 
3618 hidnplayr 66
include '../../macros.inc'
3545 hidnplayr 67
purge mov,add,sub
3618 hidnplayr 68
include '../../proc32.inc'
69
include '../../dll.inc'
70
include '../../struct.inc'
71
include '../../libio.inc'
3545 hidnplayr 72
 
3618 hidnplayr 73
include '../../network.inc'
3545 hidnplayr 74
 
75
macro sendFTP str {
76
local string, length
77
        xor     edi, edi
78
        mcall   send, [ebp + thread_data.socketnum], string, length
3819 hidnplayr 79
        invoke  con_write_asciiz, string
3545 hidnplayr 80
 
81
iglobal
3819 hidnplayr 82
string  db str, 13, 10, 0
83
length = $ - string - 1
3545 hidnplayr 84
\}
85
}
86
 
87
include 'commands.inc'
88
 
89
start:
90
        mcall   68, 11                  ; init heap
3819 hidnplayr 91
        mcall   40, EVM_STACK           ; we only want network events
3545 hidnplayr 92
 
93
; load libraries
94
        stdcall dll.Load, @IMPORT
95
        test    eax, eax
96
        jnz     exit
97
 
98
; find path to main settings file (ftpd.ini)
99
        mov     edi, path               ; Calculate the length of zero-terminated string
100
        xor     al, al
101
        mov     ecx, 1024
102
        repne   scasb
103
        dec     edi
104
        mov     esi, str_ini            ; append it with '.ini', 0
105
        movsd
106
        movsb
107
 
108
; now create the second path (users.ini)
109
        std
110
        mov     al, '/'
111
        repne   scasb
112
        lea     ecx, [edi - path + 2]
113
        cld
114
        mov     esi, path
115
        mov     edi, path2
116
        rep     movsb
117
        mov     esi, str_users
118
        movsd
119
        movsd
120
        movsw
121
 
122
; initialize console
123
        invoke  con_start, 1
124
        invoke  con_init, -1, -1, -1, -1, title
125
 
126
; get settings from ini
127
        invoke  ini.get_str, path, str_ftpd, str_ip, ini_buf, 16, 0
128
        mov     esi, ini_buf
129
        mov     cl, '.'
130
        call    ip_to_dword
131
        mov     [serverip], ebx
132
 
133
        invoke  ini.get_int, path, str_ftpd, str_port, 21
134
        xchg    al, ah
135
        mov     [sockaddr1.port], ax
136
 
137
        xchg    al, ah
138
        invoke  con_printf, str1, eax
139
        add     esp, 8
140
 
141
; open listening socket
3819 hidnplayr 142
        mcall   socket, AF_INET4, SOCK_STREAM, SO_NONBLOCK      ; we dont want to block on accept
3545 hidnplayr 143
        cmp     eax, -1
144
        je      sock_err
145
        mov     [socketnum], eax
146
 
147
        invoke  con_write_asciiz, str2
148
 
149
;        mcall   setsockopt, [socketnum], SOL_SOCKET, SO_REUSEADDR, &yes,
150
;        cmp     eax, -1
151
;        je      opt_err
152
 
153
        mcall   bind, [socketnum], sockaddr1, sockaddr1.length
154
        cmp     eax, -1
155
        je      bind_err
156
 
157
        invoke  con_write_asciiz, str2
158
 
159
        invoke  ini.get_int, path, str_ftpd, str_conn, 1        ; Backlog (max connections)
160
        mov     edx, eax
161
 
162
        invoke  con_write_asciiz, str2
163
 
164
        mcall   listen, [socketnum]
165
        cmp     eax, -1
166
        je      listen_err
167
 
168
        invoke  con_write_asciiz, str2b
169
 
170
        invoke  ini.get_int, path, str_pasv, str_start, 2000
171
        mov     [pasv_start], ax
172
        invoke  ini.get_int, path, str_pasv, str_end, 5000
173
        mov     [pasv_end], ax
174
 
175
        mov     [alive], 1
176
 
177
mainloop:
178
        mcall   23, 100                         ; Wait here for incoming connections on the base socket (socketnum)
179
                                                ; One second timeout, we will use this to check if console is still working
180
 
181
        test    eax, eax                        ; network event?
182
        jz      .checkconsole
183
 
184
if DEBUG
185
        jmp     threadstart
186
else
187
        mcall   51, 1, threadstart, 0           ; Start a new thread for every incoming connection
188
                                                ; NOTE: upon initialisation of the thread, stack will not be available!
189
end if
190
        jmp     mainloop
191
 
192
  .checkconsole:
193
 
194
        invoke  con_get_flags                   ; Is console still running?
195
        test    eax, 0x0200
196
        jz      mainloop
197
        mcall   close, [socketnum]              ; kill the listening socket
198
        mov     [alive], 0
199
        mcall   -1                              ; and exit
200
 
201
        diff16  "threadstart", 0, $
202
 
203
threadstart:
204
;;;        mcall   68, 11                          ; init heap
205
        mcall   68, 12, sizeof.thread_data      ; allocate the thread data struct
206
        test    eax, eax
207
        je      exit
208
 
209
        lea     esp, [eax + thread_data.stack]  ; init stack
210
        mov     ebp, eax
211
 
3819 hidnplayr 212
        mcall   40, EVM_STACK                   ; we only want network events for this thread
3545 hidnplayr 213
 
214
        lea     ebx, [ebp + thread_data.buffer] ; get information about the current process
215
        or      ecx, -1
216
        mcall   9
217
        mov     eax, dword [ebp + thread_data.buffer + 30]              ; PID is at offset 30
218
        mov     [ebp + thread_data.pid], eax
219
 
220
        invoke  con_set_flags, 0x03
221
        invoke  con_printf, str8, [ebp + thread_data.pid]               ; print on the console that we have created the new thread successfully
222
        add     esp, 8                                                  ; balance stack
223
        invoke  con_set_flags, 0x07
224
 
225
        mcall   accept, [socketnum], sockaddr1, sockaddr1.length        ; time to accept the awaiting connection..
226
        cmp     eax, -1
227
        je      thread_exit
228
        mov     [ebp + thread_data.socketnum], eax
229
 
230
if DEBUG
231
        mcall   close, [socketnum]                                      ; close the listening socket
232
end if
233
 
234
        mov     [ebp + thread_data.state], STATE_CONNECTED
235
        mov     [ebp + thread_data.permissions], 0
236
        mov     [ebp + thread_data.mode], MODE_NOTREADY
237
        lea     eax, [ebp + thread_data.buffer]
238
        mov     [ebp + thread_data.buffer_ptr], eax
239
        mov     [ebp + thread_data.passivesocknum], -1
240
 
241
        sendFTP "220 Welcome to KolibriOS FTP daemon"
242
 
243
        diff16  "threadloop", 0, $
244
threadloop:
3819 hidnplayr 245
;; Check if our socket is still connected
246
;        mcall   send, [ebp + thread_data.socketnum], 0, 0       ; Try to send zero bytes, if socket is closed, this will return -1
247
;        cmp     eax, -1
248
;        je      thread_exit
3545 hidnplayr 249
 
250
        cmp     [alive], 0                                      ; Did main thread take a run for it?
251
        je      thread_exit
252
 
3819 hidnplayr 253
        mcall   23, 100                                         ; Wait for network event
254
        test    eax, eax
255
        jz      threadloop
3545 hidnplayr 256
 
257
        cmp     [ebp + thread_data.mode], MODE_PASSIVE_WAIT
258
        jne     .not_passive
259
        mov     ecx, [ebp + thread_data.passivesocknum]
260
        lea     edx, [ebp + thread_data.datasock]
261
        mov     esi, sizeof.thread_data.datasock
262
        mcall   accept
263
        cmp     eax, -1
264
        je      .not_passive
265
        mov     [ebp + thread_data.datasocketnum], eax
266
        mov     [ebp + thread_data.mode], MODE_PASSIVE_OK
267
        mcall   close   ; [ebp + thread_data.passivesocknum]
268
        mov     [ebp + thread_data.passivesocknum], -1
269
 
270
        invoke  con_write_asciiz, str_datasock
271
  .not_passive:
272
 
273
        mov     ecx, [ebp + thread_data.socketnum]
274
        mov     edx, [ebp + thread_data.buffer_ptr]
275
        mov     esi, sizeof.thread_data.buffer    ;;; FIXME
3704 hidnplayr 276
        mov     edi, MSG_DONTWAIT
3545 hidnplayr 277
        mcall   recv
278
        inc     eax                                     ; error? (-1)
279
        jz      threadloop
280
        dec     eax                                     ; 0 bytes read?
281
        jz      threadloop
282
 
283
        mov     edi, [ebp + thread_data.buffer_ptr]
284
        add     [ebp + thread_data.buffer_ptr], eax
285
 
286
; Check if we received a newline character, if not, wait for more data
287
        mov     ecx, eax
3819 hidnplayr 288
        mov     al, 10
3545 hidnplayr 289
        repne   scasb
290
        jne     threadloop
291
 
3819 hidnplayr 292
        cmp     word[edi-1], 0x0a0d
293
        jne     .got_command
294
        dec     edi
295
 
3545 hidnplayr 296
; We got a command!
3819 hidnplayr 297
  .got_command:
298
        mov     byte [edi], 0                           ; append string with zero byte
3545 hidnplayr 299
        lea     esi, [ebp + thread_data.buffer]
300
        mov     ecx, [ebp + thread_data.buffer_ptr]
301
        sub     ecx, esi
302
        mov     [ebp + thread_data.buffer_ptr], esi     ; reset buffer ptr
303
 
304
        invoke  con_set_flags, 0x02                     ; print received data to console (in green color)
305
        invoke  con_write_asciiz, str_newline
306
        invoke  con_write_asciiz, esi
307
        invoke  con_set_flags, 0x07
308
 
309
        push    threadloop
310
        jmp     parse_cmd
311
 
312
listen_err:
313
        invoke  con_set_flags, 0x0c                     ; print errors in red
314
        invoke  con_write_asciiz, str3
315
        jmp     done
316
 
317
bind_err:
318
        invoke  con_set_flags, 0x0c                     ; print errors in red
319
        invoke  con_write_asciiz, str4
320
        jmp     done
321
 
322
sock_err:
323
        invoke  con_set_flags, 0x0c                     ; print errors in red
324
        invoke  con_write_asciiz, str6
325
        jmp     done
326
 
327
done:
328
        invoke  con_exit, 0
329
exit:
330
        mcall   -1
331
 
332
 
333
thread_exit:
334
        invoke  con_set_flags, 0x03                             ; print thread info in blue
335
        invoke  con_printf, str_bye, [ebp + thread_data.pid]    ; print on the console that we are about to kill the thread
336
        add     esp, 8                                          ; balance stack
337
        mcall   68, 13, ebp                                     ; free the memory
338
        mcall   -1                                              ; and kill the thread
339
 
340
 
341
; initialized data
342
 
3819 hidnplayr 343
title           db 'FTP daemon', 0
3545 hidnplayr 344
str1            db 'Starting FTP daemon on port %u.', 0
345
str2            db '.', 0
346
str2b           db ' OK!',10,0
347
str3            db 'Listen error',10,0
348
str4            db 10,'ERROR: local port is already in use.',10,0
349
;str5            db 'Setsockopt error.',10,10,0
350
str6            db 'ERROR: Could not open socket.',10,0
351
str7            db 'Got data!',10,10,0
352
str8            db 10,'Thread %d created',10,0
353
str_bye         db 10,'Thread %d killed',10,0
354
 
355
str_logged_in   db 'Login ok',10,0
356
str_pass_ok     db 'Password ok',10,0
357
str_pass_err    db 'Password/Username incorrect',10,0
358
str_pwd         db 'Current directory is "%s"\n',0
359
str_err2        db 'ERROR: cannot open the directory.',10,0
360
str_datasock    db 'Passive data socket connected.',10,0
361
str_notfound    db 'ERROR: file not found.',10,0
362
str_sockerr     db 'ERROR: socket error.',10,0
363
 
364
str_newline     db 10, 0
365
str_mask        db '*', 0
366
str_infinity    db 0xff, 0xff, 0xff, 0xff, 0
367
 
368
months          dd 'Jan '
369
                dd 'Feb '
370
                dd 'Mar '
371
                dd 'Apr '
372
                dd 'May '
373
                dd 'Jun '
374
                dd 'Jul '
375
                dd 'Aug '
376
                dd 'Sep '
377
                dd 'Oct '
378
                dd 'Nov '
379
                dd 'Dec '
380
 
381
str_users       db 'users'
382
str_ini         db '.ini', 0
383
str_port        db 'port', 0
384
str_ftpd        db 'ftpd', 0
385
str_conn        db 'conn', 0
386
str_ip          db 'ip', 0
387
str_pass        db 'pass', 0
388
str_home        db 'home', 0
389
str_mode        db 'mode', 0
390
str_pasv        db 'pasv', 0
391
str_start       db 'start', 0
392
str_end         db 'end', 0
393
 
394
 
395
sockaddr1:
396
                dw AF_INET4
397
  .port         dw 0
398
  .ip           dd 0
399
                rb 10
400
  .length       = $ - sockaddr1
401
 
402
; import
403
 
404
align 4
405
@IMPORT:
406
 
407
diff16 "import", 0, $
408
 
409
library console,                'console.obj',\
410
        libini,                 'libini.obj', \
411
        libio,                  'libio.obj'
412
 
413
import  console,\
414
        con_start,              'START',\
415
        con_init,               'con_init',\
416
        con_write_asciiz,       'con_write_asciiz',\
417
        con_exit,               'con_exit',\
418
        con_gets,               'con_gets',\
419
        con_cls,                'con_cls',\
420
        con_printf,             'con_printf',\
421
        con_getch2,             'con_getch2',\
422
        con_set_cursor_pos,     'con_set_cursor_pos',\
423
        con_set_flags,          'con_set_flags',\
424
        con_get_flags,          'con_get_flags'
425
 
426
import  libini,\
427
        ini.get_str,            'ini_get_str',\
428
        ini.get_int,            'ini_get_int'
429
 
430
import  libio,\
431
        file.size,              'file_size',\
432
        file.open,              'file_open',\
433
        file.read,              'file_read',\
434
        file.close,             'file_close',\
435
        file.find.first,        'file_find_first',\
436
        file.find.next,         'file_find_next',\
437
        file.find.close,        'file_find_close'
438
 
439
 
440
IncludeIGlobals
441
 
442
 
443
i_end:
444
 
445
diff16 "i_end", 0, $
446
 
447
; uninitialised data
448
 
449
        socketnum       dd ?
450
        path            rb 1024
451
        path2           rb 1024
452
        params          rb 1024
453
        serverip        dd ?
454
        pasv_start      dw ?
455
        pasv_end        dw ?
456
        pasv_port       dw ?
457
 
458
        ini_buf         rb 3*4+3+1
459
 
460
        alive           db ?
461
 
462
mem:
463