From: William James Date: 2006-12-29T05:15:11+09:00 Subject: Re: unsafe readline(), anything better? Rob Muhlestein wrote: > Looking for a combination of readpartial() and readline() in order to > safely read a line of maxlen. IO.readline() appears to suffer from lacking > this maximum allowing DOS against servers reading HTTP headers, for > example, just by using sock.readline() alone, which is what > Net::HTTPResponse and others do: > > 1974 class << HTTPResponse > 1975 def read_new(sock) #:nodoc: internal use only 1976 httpv, > code, msg = read_status_line(sock) 1977 res = > response_class(code).new(httpv, code, msg) 1978 > each_response_header(sock) do |k,v| 1979 res.add_field k, v > 1980 end > 1981 res > 1982 end > 1983 > 1984 private > 1985 > 1986 def read_status_line(sock) 1987 str = sock.readline > 1988 m = > /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/in.match(str) o r > 1989 raise HTTPBadResponse, "wrong status line: #{str.dump}" > 1990 m.captures > 1991 end > > I know this is fundamentally a problem with the popular readline C libs > out there. Wish they had an nreadline like the sprintf and snprintf > additional function. Sure I could readchar() or readbytes() watching each > read for a newline, but that is just unfun. Have I overlooked something > obvious in my search? Hoping to not have to write my own safe/buffered IO > layer like I've had to do with other langs. > > If there is enough interest, maybe I'll hack a readmaxline() method into > an IO patch to submit. Actually, on second thought, how about adding a > second parameter: > > ios.readline(sep_string=$/,maxlen=nil) > > The tough question would then be whether to raise an LineTooLong exception > or just return what could be read of the line up to that point. > > Thanks, > > Rob class File def safe_readline(sep_string=$/,maxlen=nil) buf_size = 1024 line = "" while !self.eof? s = read( [ buf_size, maxlen - line.size ].min ) line << s if i = line.index( sep_string ) line = line[0,i+sep_string.size] return [ line, true ] end return [ line[0,maxlen], false ] if maxlen && line.size >= maxlen end [ line, true ] end end open('junk'){|h| p h.safe_readline("\n",9) }