diff --git a/client/inc/nb65_constants.i b/client/inc/nb65_constants.i index fda1542..06b32b7 100644 --- a/client/inc/nb65_constants.i +++ b/client/inc/nb65_constants.i @@ -124,5 +124,6 @@ NB65_ERROR_CONNECTION_RESET_BY_PEER EQU $89 NB65_ERROR_CONNECTION_CLOSED EQU $8A NB65_ERROR_FILE_ACCESS_FAILURE EQU $90 NB65_MALFORMED_URL EQU $A0 +NB65_DNS_LOOKUP_FAILED EQU $A1 NB65_ERROR_OPTION_NOT_SUPPORTED EQU $FE NB65_ERROR_FUNCTION_NOT_SUPPORTED EQU $FF diff --git a/client/ip65/ip65.s b/client/ip65/ip65.s index 1ec332d..ed59aac 100644 --- a/client/ip65/ip65.s +++ b/client/ip65/ip65.s @@ -52,7 +52,8 @@ ip65_error: .res 1 ;last error code ;outputs: AX set to a pseudo-random 16 bit number ip65_random_word: jsr timer_read ;sets AX - adc $d018 ;on a c64, this is the raster register +; adc $d018 ;on a c64, this is the raster register + adc $d41b; on a c64, this is a 'random' number from the SID pha adc ip65_ctr_arp ora #$08 ;make sure we grab at least 8 bytes from eth_inp diff --git a/client/ip65/string_utils.s b/client/ip65/string_utils.s index 9a3f2a6..ebcf875 100644 --- a/client/ip65/string_utils.s +++ b/client/ip65/string_utils.s @@ -1,6 +1,4 @@ ;text file parsing routines -; first call parser_init -; then call parser_skip_next .export parse_integer .importzp copy_dest diff --git a/client/ip65/tcp.s b/client/ip65/tcp.s index 7d5b896..947f03e 100644 --- a/client/ip65/tcp.s +++ b/client/ip65/tcp.s @@ -23,6 +23,7 @@ MAX_TCP_PACKETS_SENT=8 ;timeout after sending 8 messages will be about 7 sec .export tcp_connect_ip .export tcp_send_data_len .export tcp_send +.export tcp_send_string .export tcp_close .export tcp_listen @@ -368,6 +369,33 @@ tcp_close: rts +tcp_send_string: +;send a string over the current tcp connection +;inputs: +; tcp connection should already be opened +; AX: pointer to buffer - data up to (but not including) +; the first nul byte will be sent. max of 255 bytes will be sent. +;outputs: +; carry flag is set if an error occured, clear otherwise + + stax tcp_send_data_ptr + stax copy_src + lda #0 + tay + sta tcp_send_data_len + sta tcp_send_data_len+1 + lda (copy_src),y + bne @find_end_of_string + rts ; if the string is empty, don't send anything! +@find_end_of_string: + lda (copy_src),y + beq @done + inc tcp_send_data_len + iny + bne @find_end_of_string +@done: + ldax tcp_send_data_ptr + ;now we can fall through into tcp_send tcp_send: ;send tcp data ;inputs: @@ -378,6 +406,7 @@ tcp_send: ; carry flag is set if an error occured, clear otherwise stax tcp_send_data_ptr + lda tcp_state cmp #tcp_cxn_state_established beq @connection_established @@ -646,8 +675,19 @@ tcp_process: bit tcp_inp+tcp_flags_field beq @not_reset jsr check_current_connection - bcs @not_current_connection_on_rst - ;connection has been reset so mark it as closed + bcs @not_current_connection_on_rst + ;for some reason, search.twitter.com is sending RSTs with ID=$1234 (i.e. echoing the inbound ID) + ;but then keeps the connection open and ends up sending the file. + ;so lets ignore a reset with ID=$1234 + lda ip_inp+ip_id + cmp #$34 + bne @not_invalid_reset + lda ip_inp+ip_id+1 + cmp #$12 + bne @not_invalid_reset + jmp @send_ack +@not_invalid_reset: + ;connection has been reset so mark it as closed lda #tcp_cxn_state_closed sta tcp_state lda #NB65_ERROR_CONNECTION_RESET_BY_PEER diff --git a/client/ip65/url.s b/client/ip65/url.s index bb8d2b1..ce3f617 100644 --- a/client/ip65/url.s +++ b/client/ip65/url.s @@ -11,18 +11,34 @@ .import output_buffer .importzp copy_src .importzp copy_dest +.import copymem + .import ip65_error +.import ip65_process .import parser_init .import parser_skip_next .import dns_set_hostname .import dns_resolve .import parse_integer .import dns_ip +.import tcp_connect +.import tcp_send_string +.import tcp_send_data_len +.import tcp_callback +.import tcp_close +.import tcp_connect_ip +.import tcp_inbound_data_length +.import tcp_inbound_data_ptr + + .export url_ip .export url_port .export url_selector .export url_resource_type .export url_parse +.export url_download +.export url_download_buffer +.export url_download_buffer_length target_string=copy_src search_string=copy_dest @@ -41,6 +57,16 @@ selector_buffer=output_buffer src_ptr: .res 1 dest_ptr: .res 1 + + url_download_buffer: .res 2 ; points to a buffer that url will be downloaded into + url_download_buffer_length: .res 2 ;length of buffer that url will be downloaded into + + temp_buffer: .res 2 + temp_buffer_length: .res 2 + + download_flag: .res 1 + + .code @@ -102,7 +128,11 @@ lda #url_type_gopher jsr dns_set_hostname bcs @exit_with_sec jsr dns_resolve - bcs @exit_with_sec + bcc :+ + lda #NB65_DNS_LOOKUP_FAILED + sta ip65_error + jmp @exit_with_sec + : ;copy IP address ldx #3 : @@ -138,19 +168,23 @@ lda #url_type_gopher ;first byte after / in a gopher url is the resource type ldy src_ptr lda (copy_src),y + beq @start_of_selector sta url_resource_type inc src_ptr jmp @start_of_selector @not_gopher: cmp #url_type_http - bne @done ; if it's not gopher or http, we don't know how to build a selector - ldy #3 + beq @build_http_request + jmp @done ; if it's not gopher or http, we don't know how to build a selector +@build_http_request: + ldy #get_length-1 sty dest_ptr : lda get,y sta (copy_dest),y dey bpl :- + @start_of_selector: lda #'/' inc dest_ptr @@ -166,6 +200,55 @@ lda #url_type_gopher inc dest_ptr bne @copy_one_byte @end_of_selector: + + + ldx #1 ;number of CRLF at end of gopher request + lda url_type + + cmp #url_type_http + bne @final_crlf + + ;now the HTTP version number & Host: field + ldx #0 +: + lda http_version_and_host,x + beq :+ + ldy dest_ptr + inc dest_ptr + sta (copy_dest),y + inx + bne :- +: + + + ;now copy the host field + jsr skip_to_hostname + ;AX now pointing at hostname + stax copy_src + ldax #selector_buffer + stax copy_dest + + lda #0 + sta src_ptr + +@copy_one_byte_of_hostname: + ldy src_ptr + lda (copy_src),y + beq @end_of_hostname + cmp #':' + beq @end_of_hostname + cmp #'/' + beq @end_of_hostname + inc src_ptr + ldy dest_ptr + sta (copy_dest),y + inc dest_ptr + bne @copy_one_byte_of_hostname +@end_of_hostname: + + ldx #2 ;number of CRLF at end of HTTP request + +@final_crlf: ldy dest_ptr lda #$0d sta (copy_dest),y @@ -173,11 +256,16 @@ lda #url_type_gopher lda #$0a sta (copy_dest),y iny + dex + bne @final_crlf + @done: lda #$00 sta (copy_dest),y ldax #selector_buffer + stax url_selector clc + rts skip_to_hostname: @@ -186,7 +274,135 @@ skip_to_hostname: ldax #colon_slash_slash jmp parser_skip_next + .code + + +;download a resource specified by an URL +;inputs: +;AX = address of URL string +; url_download_buffer - points to a buffer that url will be downloaded into +; url_download_buffer_length - length of buffer +;outputs: +; sec if an error occured, else buffer pointed at by url_download_buffer is filled with contents +; of specified resource (with an extra 2 null bytes at the end), +; AX = length of resource downloaded. +url_download: + jsr url_parse + bcc @url_parsed_ok + rts +@url_parsed_ok: + ldax url_download_buffer + stax temp_buffer + ldax url_download_buffer_length + stax temp_buffer_length + + ldx #3 ; save IP address just retrieved +: lda url_ip,x + sta tcp_connect_ip,x + dex + bpl :- + ldax #url_download_callback + stax tcp_callback + + ldax url_port + jsr tcp_connect + bcs @error + + ;connected, now send the selector + ldx #0 + stx download_flag + ldax url_selector + + jsr tcp_send_string + ;now loop until we're done +@download_loop: + jsr ip65_process + lda download_flag + beq @download_loop + jsr tcp_close + clc +@error: + rts + + + lda #NB65_ERROR_FILE_ACCESS_FAILURE + sta ip65_error + sec + rts + + + url_download_callback: + + lda tcp_inbound_data_length+1 + cmp #$ff + bne @not_end_of_file +@end_of_file: + lda #1 + sta download_flag + + ;put a zero byte at the end of the file (in case it was a text file) + ldax temp_buffer + stax copy_dest + lda #0 + tay + sta (copy_dest),y + rts +@not_end_of_file: + +;copy this chunk to our input buffer + ldax temp_buffer + stax copy_dest + ldax tcp_inbound_data_ptr + stax copy_src + sec + lda temp_buffer_length + sbc tcp_inbound_data_length + pha + lda temp_buffer_length+1 + sbc tcp_inbound_data_length+1 + bcc @would_overflow_buffer + sta temp_buffer_length+1 + pla + sta temp_buffer_length + ldax tcp_inbound_data_length + jsr copymem +;increment the pointer into the input buffer + clc + lda temp_buffer + adc tcp_inbound_data_length + sta temp_buffer + lda temp_buffer+1 + adc tcp_inbound_data_length+1 + sta temp_buffer+1 +; lda #'*' +; jsr print_a + + rts +@would_overflow_buffer: + pla ;clean up the stack + ldax temp_buffer_length + jsr copymem + lda temp_buffer + adc temp_buffer_length + sta temp_buffer + lda temp_buffer+1 + adc temp_buffer_length+1 + sta temp_buffer+1 + lda #0 + sta temp_buffer_length + sta temp_buffer_length+1 + rts + + .rodata get: .byte "GET " + get_length=4 + http_version_and_host: .byte " HTTP/1.1",$0d,$0a, "Host: ",0 +; http_trailer: .byte " HTTP/1.1",$0a,$0a +; http_trailer_end: +; http_trailer_length=http_trailer_end-http_trailer + colon_slash_slash: .byte ":/" slash: .byte "/",0 - colon: .byte ":",0 \ No newline at end of file + colon: .byte ":",0 + + \ No newline at end of file diff --git a/client/test/Makefile b/client/test/Makefile index 6ee55ea..3058902 100644 --- a/client/test/Makefile +++ b/client/test/Makefile @@ -31,6 +31,7 @@ all: \ testdottedquad.prg\ test_tcp.prg \ test_parser.prg \ + test_get_url.prg \ %.o: %.c $(CC) -c $(CFLAGS) $< @@ -44,6 +45,11 @@ all: \ test_tcp.prg: test_tcp.o $(IP65TCPLIB) $(C64PROGLIB) $(INCFILES) ../cfg/c64prg.cfg $(LD) -m test_tcp.map -vm -C ../cfg/c64prg.cfg -o test_tcp.prg $(AFLAGS) $< $(IP65TCPLIB) $(C64PROGLIB) +test_parser.prg: test_parser.o $(IP65TCPLIB) $(C64PROGLIB) $(INCFILES) ../cfg/c64prg.cfg + $(LD) -m test_parser.map -vm -C ../cfg/c64prg.cfg -o test_parser.prg $(AFLAGS) $< $(IP65TCPLIB) $(C64PROGLIB) + +test_get_url.prg: test_get_url.o $(IP65TCPLIB) $(C64PROGLIB) $(INCFILES) ../cfg/c64prg.cfg + $(LD) -m test_get_url.map -vm -C ../cfg/c64prg.cfg -o test_get_url.prg $(AFLAGS) $< $(IP65TCPLIB) $(C64PROGLIB) %.pg2: %.o $(IP65LIB) $(APPLE2PROGLIB) $(INCFILES) ../cfg/a2bin.cfg $(LD) -C ../cfg/a2bin.cfg -o $*.pg2 $(AFLAGS) $< $(IP65LIB) $(APPLE2PROGLIB) diff --git a/client/test/test_parser.s b/client/test/test_parser.s index b12bd92..d7bbf34 100644 --- a/client/test/test_parser.s +++ b/client/test/test_parser.s @@ -209,7 +209,7 @@ selector: .asciiz "SELECTOR: " press_a_key: .byte "PRESS ANY KEY TO CONTINUE",13,0 atom_file: -.incbin "atom_test.xml" +;.incbin "atom_test.xml" .byte 0 \ No newline at end of file