Subversion Repositories Kolibri OS

Rev

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
;;                                                                 ;;
6835 hidnplayr 3
;; Copyright (C) KolibriOS team 2010-2017. 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'
6835 hidnplayr 71
include '../../develop/libraries/libs-dev/libio/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
3824 hidnplayr 361
str_datasock2   db 'Active data socket connected.',10,0
362
str_alopen      db 'Data connection already open.',10,0
3545 hidnplayr 363
str_notfound    db 'ERROR: file not found.',10,0
364
str_sockerr     db 'ERROR: socket error.',10,0
365
 
366
str_newline     db 10, 0
367
str_mask        db '*', 0
368
str_infinity    db 0xff, 0xff, 0xff, 0xff, 0
369
 
370
months          dd 'Jan '
371
                dd 'Feb '
372
                dd 'Mar '
373
                dd 'Apr '
374
                dd 'May '
375
                dd 'Jun '
376
                dd 'Jul '
377
                dd 'Aug '
378
                dd 'Sep '
379
                dd 'Oct '
380
                dd 'Nov '
381
                dd 'Dec '
382
 
383
str_users       db 'users'
384
str_ini         db '.ini', 0
385
str_port        db 'port', 0
386
str_ftpd        db 'ftpd', 0
387
str_conn        db 'conn', 0
388
str_ip          db 'ip', 0
389
str_pass        db 'pass', 0
390
str_home        db 'home', 0
391
str_mode        db 'mode', 0
392
str_pasv        db 'pasv', 0
393
str_start       db 'start', 0
394
str_end         db 'end', 0
395
 
396
 
397
sockaddr1:
398
                dw AF_INET4
399
  .port         dw 0
400
  .ip           dd 0
401
                rb 10
402
  .length       = $ - sockaddr1
403
 
404
; import
405
 
406
align 4
407
@IMPORT:
408
 
409
diff16 "import", 0, $
410
 
411
library console,                'console.obj',\
412
        libini,                 'libini.obj', \
413
        libio,                  'libio.obj'
414
 
415
import  console,\
416
        con_start,              'START',\
417
        con_init,               'con_init',\
418
        con_write_asciiz,       'con_write_asciiz',\
419
        con_exit,               'con_exit',\
420
        con_gets,               'con_gets',\
421
        con_cls,                'con_cls',\
422
        con_printf,             'con_printf',\
423
        con_getch2,             'con_getch2',\
424
        con_set_cursor_pos,     'con_set_cursor_pos',\
425
        con_set_flags,          'con_set_flags',\
426
        con_get_flags,          'con_get_flags'
427
 
428
import  libini,\
429
        ini.get_str,            'ini_get_str',\
430
        ini.get_int,            'ini_get_int'
431
 
432
import  libio,\
433
        file.size,              'file_size',\
434
        file.open,              'file_open',\
435
        file.read,              'file_read',\
436
        file.close,             'file_close',\
437
        file.find.first,        'file_find_first',\
438
        file.find.next,         'file_find_next',\
439
        file.find.close,        'file_find_close'
440
 
441
 
442
IncludeIGlobals
443
 
444
 
445
i_end:
446
 
447
diff16 "i_end", 0, $
448
 
449
; uninitialised data
450
 
451
        socketnum       dd ?
452
        path            rb 1024
453
        path2           rb 1024
454
        params          rb 1024
455
        serverip        dd ?
456
        pasv_start      dw ?
457
        pasv_end        dw ?
458
        pasv_port       dw ?
459
 
460
        ini_buf         rb 3*4+3+1
461
 
462
        alive           db ?
463
 
464
mem:
465