parse.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. package smtp
  2. // Parse RFC5321 productions, no regex
  3. import (
  4. "bytes"
  5. "errors"
  6. "net"
  7. "strconv"
  8. "strings"
  9. )
  10. const (
  11. // The maximum total length of a reverse-path or forward-path is 256
  12. LimitPath = 256
  13. // The maximum total length of a user name or other local-part is 64
  14. // however, here we double it, since a few major services don't respect that and go over
  15. LimitLocalPart = 64 * 2
  16. // //The maximum total length of a domain name or number is 255
  17. LimitDomain = 255
  18. // The minimum total number of recipients that must be buffered is 100
  19. LimitRecipients = 100
  20. )
  21. type PathParam []string
  22. type TransportType int
  23. const (
  24. TransportType7bit TransportType = iota
  25. TransportType8bit
  26. TransportTypeUnspecified
  27. TransportTypeInvalid
  28. )
  29. // is8BitMime checks for the BODY parameter as
  30. func (p PathParam) Transport() TransportType {
  31. if len(p) != 2 {
  32. return TransportTypeUnspecified
  33. }
  34. if strings.ToUpper(p[0]) != "BODY" {
  35. // this is not a 'BODY' param
  36. return TransportTypeUnspecified
  37. }
  38. if strings.ToUpper(p[1]) == "8BITMIME" {
  39. return TransportType8bit
  40. } else if strings.ToUpper(p[1]) == "7BIT" {
  41. return TransportType7bit
  42. }
  43. return TransportTypeInvalid
  44. }
  45. var atExpected = errors.New("@ expected as part of mailbox")
  46. // Parse Email Addresses according to https://tools.ietf.org/html/rfc5321
  47. type Parser struct {
  48. NullPath bool
  49. LocalPart string
  50. LocalPartQuotes bool // does the local part need quotes?
  51. Domain string
  52. IP net.IP
  53. ADL []string
  54. PathParams []PathParam
  55. pos int
  56. ch byte
  57. buf []byte
  58. accept bytes.Buffer
  59. }
  60. func NewParser(buf []byte) *Parser {
  61. s := new(Parser)
  62. s.buf = buf
  63. s.pos = -1
  64. return s
  65. }
  66. func (s *Parser) Reset() {
  67. s.buf = s.buf[:0]
  68. if s.pos != -1 {
  69. s.pos = -1
  70. s.ADL = nil
  71. s.PathParams = nil
  72. s.NullPath = false
  73. s.LocalPart = ""
  74. s.Domain = ""
  75. s.accept.Reset()
  76. s.LocalPartQuotes = false
  77. s.IP = nil
  78. }
  79. }
  80. func (s *Parser) set(input []byte) {
  81. s.Reset()
  82. s.buf = input
  83. }
  84. func (s *Parser) next() byte {
  85. s.pos++
  86. if s.pos < len(s.buf) {
  87. s.ch = s.buf[s.pos]
  88. return s.ch
  89. }
  90. return 0
  91. }
  92. func (s *Parser) peek() byte {
  93. if s.pos+1 < len(s.buf) {
  94. return s.buf[s.pos+1]
  95. }
  96. return 0
  97. }
  98. func (s *Parser) reversePath() (err error) {
  99. if s.peek() == ' ' {
  100. s.next() // tolerate a space at the front
  101. }
  102. if i := bytes.Index(s.buf[s.pos+1:], []byte{'<', '>'}); i == 0 {
  103. s.NullPath = true
  104. return nil
  105. }
  106. if err = s.path(); err != nil {
  107. return err
  108. }
  109. return nil
  110. }
  111. func (s *Parser) forwardPath() (err error) {
  112. if s.peek() == ' ' {
  113. s.next() // tolerate a space at the front
  114. }
  115. if err = s.path(); err != nil && err != atExpected {
  116. return err
  117. }
  118. // special case for forwardPath only - can just be addressed to postmaster
  119. if i := strings.Index(strings.ToLower(s.LocalPart), postmasterLocalPart); i == 0 {
  120. s.LocalPart = postmasterLocalPart
  121. return nil // atExpected will be ignored, postmaster doesn't need @
  122. }
  123. return err // it may return atExpected
  124. }
  125. //MailFrom accepts the following syntax: Reverse-path [SP Mail-parameters] CRLF
  126. func (s *Parser) MailFrom(input []byte) (err error) {
  127. s.set(input)
  128. if err := s.reversePath(); err != nil {
  129. return err
  130. }
  131. s.next()
  132. if p := s.next(); p == ' ' {
  133. // parse Rcpt-parameters
  134. // The optional <mail-parameters> are associated with negotiated SMTP
  135. // service extensions
  136. if tup, err := s.parameters(); err != nil {
  137. return errors.New("param parse error")
  138. } else if len(tup) > 0 {
  139. s.PathParams = tup
  140. }
  141. }
  142. return nil
  143. }
  144. const postmasterLocalPart = "postmaster"
  145. //RcptTo accepts the following syntax: ( "<Postmaster@" Domain ">" / "<Postmaster>" /
  146. // Forward-path ) [SP Rcpt-parameters] CRLF
  147. func (s *Parser) RcptTo(input []byte) (err error) {
  148. s.set(input)
  149. if err := s.forwardPath(); err != nil {
  150. return err
  151. }
  152. s.next()
  153. if p := s.next(); p == ' ' {
  154. // parse Rcpt-parameters
  155. if tup, err := s.parameters(); err != nil {
  156. return errors.New("param parse error")
  157. } else if len(tup) > 0 {
  158. s.PathParams = tup
  159. }
  160. }
  161. return nil
  162. }
  163. // esmtp-param *(SP esmtp-param)
  164. func (s *Parser) parameters() ([]PathParam, error) {
  165. params := make([]PathParam, 0)
  166. for {
  167. if result, err := s.param(); err != nil {
  168. return params, err
  169. } else {
  170. params = append(params, result)
  171. }
  172. if p := s.next(); p != ' ' {
  173. return params, nil
  174. }
  175. }
  176. }
  177. func isESMTPValue(c byte) bool {
  178. if ('!' <= c && c <= '<') ||
  179. ('>' <= c && c <= '~') {
  180. return true
  181. }
  182. return false
  183. }
  184. // esmtp-param = esmtp-keyword ["=" esmtp-value]
  185. // esmtp-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-")
  186. // esmtp-value = 1*(%d33-60 / %d62-126)
  187. func (s *Parser) param() (result []string, err error) {
  188. state := 0
  189. var key, value string
  190. defer func() {
  191. result = append(result, key, value)
  192. s.accept.Reset()
  193. }()
  194. for c := s.next(); ; c = s.next() {
  195. switch state {
  196. case 0:
  197. // first char must be let-dig
  198. if !isLetDig(c) {
  199. return result, errors.New("parse error")
  200. }
  201. // accept
  202. s.accept.WriteByte(c)
  203. state = 1
  204. case 1:
  205. // *(ALPHA / DIGIT / "-")
  206. if !isLetDig(c) {
  207. if c == '=' {
  208. key = s.accept.String()
  209. s.accept.Reset()
  210. state = 2
  211. continue
  212. } else if c == '-' {
  213. // cannot have - at the end of a keyword
  214. if p := s.peek(); !isLetDig(p) && p != '-' {
  215. return result, errors.New("parse error")
  216. }
  217. s.accept.WriteByte(c)
  218. continue
  219. }
  220. key = s.accept.String()
  221. return result, nil
  222. }
  223. s.accept.WriteByte(c)
  224. case 2:
  225. // start of value, must match at least 1
  226. if !isESMTPValue(c) {
  227. return result, errors.New("parse error")
  228. }
  229. s.accept.WriteByte(c)
  230. if !isESMTPValue(s.peek()) {
  231. value = s.accept.String()
  232. return result, nil
  233. }
  234. state = 3
  235. case 3:
  236. // 1*(%d33-60 / %d62-126)
  237. s.accept.WriteByte(c)
  238. if !isESMTPValue(s.peek()) {
  239. value = s.accept.String()
  240. return result, nil
  241. }
  242. }
  243. }
  244. }
  245. // "<" [ A-d-l ":" ] Mailbox ">"
  246. func (s *Parser) path() (err error) {
  247. if s.next() == '<' && s.peek() == '@' {
  248. if err = s.adl(); err == nil {
  249. s.next()
  250. if s.ch != ':' {
  251. return errors.New("syntax error")
  252. }
  253. }
  254. }
  255. if err = s.mailbox(); err != nil {
  256. return err
  257. }
  258. if p := s.peek(); p != '>' {
  259. return errors.New("missing closing >")
  260. }
  261. return nil
  262. }
  263. // At-domain *( "," At-domain )
  264. func (s *Parser) adl() error {
  265. for {
  266. if err := s.atDomain(); err != nil {
  267. return err
  268. }
  269. s.ADL = append(s.ADL, s.accept.String())
  270. s.accept.Reset()
  271. if s.peek() != ',' {
  272. break
  273. }
  274. s.next()
  275. }
  276. return nil
  277. }
  278. // At-domain = "@" Domain
  279. func (s *Parser) atDomain() error {
  280. if s.next() == '@' {
  281. s.accept.WriteByte('@')
  282. return s.domain()
  283. }
  284. return errors.New("syntax error")
  285. }
  286. // sub-domain *("." sub-domain)
  287. func (s *Parser) domain() error {
  288. for {
  289. if err := s.subdomain(); err != nil {
  290. return err
  291. }
  292. if p := s.peek(); p != '.' {
  293. if p != ':' && p != ',' && p != '>' && p != 0 {
  294. return errors.New("domain parse error")
  295. }
  296. break
  297. }
  298. s.accept.WriteByte(s.next())
  299. }
  300. return nil
  301. }
  302. // Let-dig [Ldh-str]
  303. func (s *Parser) subdomain() error {
  304. state := 0
  305. for c := s.next(); ; c = s.next() {
  306. switch state {
  307. case 0:
  308. p := s.peek()
  309. if isLetDig(c) {
  310. s.accept.WriteByte(c)
  311. if !isLetDig(p) && p != '-' {
  312. return nil
  313. }
  314. state = 1
  315. continue
  316. }
  317. return errors.New("subdomain parse err")
  318. case 1:
  319. p := s.peek()
  320. if isLetDig(c) || c == '-' {
  321. s.accept.WriteByte(c)
  322. }
  323. if !isLetDig(p) && p != '-' {
  324. if c == '-' {
  325. return errors.New("subdomain parse err")
  326. }
  327. return nil
  328. }
  329. }
  330. }
  331. }
  332. // Local-part "@" ( Domain / address-literal )
  333. func (s *Parser) mailbox() error {
  334. defer func() {
  335. if s.accept.Len() > 0 {
  336. s.Domain = s.accept.String()
  337. s.accept.Reset()
  338. }
  339. }()
  340. err := s.localPart()
  341. if err != nil {
  342. return err
  343. }
  344. if s.ch != '@' {
  345. return atExpected
  346. }
  347. if p := s.peek(); p == '[' {
  348. return s.addressLiteral()
  349. } else {
  350. return s.domain()
  351. }
  352. }
  353. // "[" ( IPv4-address-literal /
  354. // IPv6-address-literal /
  355. // General-address-literal ) "]"
  356. func (s *Parser) addressLiteral() error {
  357. ch := s.next()
  358. if ch == '[' {
  359. p := s.peek()
  360. var err error
  361. if p == 'I' || p == 'i' {
  362. for i := 0; i < 5; i++ {
  363. s.next() // IPv6:
  364. }
  365. err = s.ipv6AddressLiteral()
  366. } else if p >= 48 && p <= 57 {
  367. err = s.ipv4AddressLiteral()
  368. }
  369. if err != nil {
  370. return err
  371. }
  372. if s.ch != ']' {
  373. return errors.New("] expected for address literal")
  374. }
  375. return nil
  376. }
  377. return nil
  378. }
  379. // Snum 3("." Snum)
  380. func (s *Parser) ipv4AddressLiteral() error {
  381. for i := 0; i < 4; i++ {
  382. if err := s.snum(); err != nil {
  383. return err
  384. }
  385. if s.ch != '.' {
  386. break
  387. }
  388. s.accept.WriteByte(s.ch)
  389. }
  390. ip := net.ParseIP(s.accept.String())
  391. if ip == nil {
  392. return errors.New("invalid ip")
  393. }
  394. s.IP = ip
  395. return nil
  396. }
  397. // 1*3DIGIT
  398. // representing a decimal integer
  399. // value accept the range 0 through 255
  400. func (s *Parser) snum() error {
  401. state := 0
  402. var num bytes.Buffer
  403. for i := 4; i > 0; i-- {
  404. c := s.next()
  405. if state == 0 {
  406. if !(c >= 48 && c <= 57) {
  407. return errors.New("snum parse error")
  408. } else {
  409. num.WriteByte(s.ch)
  410. s.accept.WriteByte(s.ch)
  411. state = 1
  412. continue
  413. }
  414. }
  415. if state == 1 {
  416. if !(c >= 48 && c <= 57) {
  417. if v, err := strconv.Atoi(num.String()); err != nil {
  418. return err
  419. } else if v >= 0 && v <= 255 {
  420. return nil
  421. } else {
  422. return errors.New("invalid ipv4")
  423. }
  424. } else {
  425. num.WriteByte(s.ch)
  426. s.accept.WriteByte(s.ch)
  427. }
  428. }
  429. }
  430. return errors.New("too many digits")
  431. }
  432. //IPv6:" IPv6-addr
  433. func (s *Parser) ipv6AddressLiteral() error {
  434. var ip bytes.Buffer
  435. for c := s.next(); ; c = s.next() {
  436. if !(c >= 48 && c <= 57) &&
  437. !(c >= 65 && c <= 70) &&
  438. !(c >= 97 && c <= 102) &&
  439. c != ':' && c != '.' {
  440. ipstr := ip.String()
  441. if v := net.ParseIP(ipstr); v != nil {
  442. s.accept.WriteString(v.String())
  443. s.IP = v
  444. return nil
  445. }
  446. return errors.New("invalid ipv6")
  447. } else {
  448. ip.WriteByte(c)
  449. }
  450. }
  451. }
  452. // Dot-string / Quoted-string
  453. func (s *Parser) localPart() error {
  454. defer func() {
  455. if s.accept.Len() > 0 {
  456. s.LocalPart = s.accept.String()
  457. s.accept.Reset()
  458. }
  459. }()
  460. p := s.peek()
  461. if p == '"' {
  462. return s.quotedString()
  463. } else {
  464. return s.dotString()
  465. }
  466. }
  467. // DQUOTE *QcontentSMTP DQUOTE
  468. func (s *Parser) quotedString() error {
  469. if s.next() == '"' {
  470. if err := s.QcontentSMTP(); err != nil {
  471. return err
  472. }
  473. if s.ch != '"' {
  474. return errors.New("quoted string not closed")
  475. } else {
  476. // accept the "
  477. s.next()
  478. }
  479. }
  480. return nil
  481. }
  482. // qtextSMTP / quoted-pairSMTP
  483. // quoted-pairSMTP = %d92 %d32-126
  484. // qtextSMTP = %d32-33 / %d35-91 / %d93-126
  485. func (s *Parser) QcontentSMTP() error {
  486. state := 0
  487. for {
  488. ch := s.next()
  489. switch state {
  490. case 0:
  491. if ch == '\\' {
  492. state = 1
  493. // s.accept.WriteByte(ch)
  494. continue
  495. } else if ch == 32 || ch == 33 ||
  496. (ch >= 35 && ch <= 91) ||
  497. (ch >= 93 && ch <= 126) {
  498. if s.LocalPartQuotes == false && !s.isAtext(ch) {
  499. s.LocalPartQuotes = true
  500. }
  501. s.accept.WriteByte(ch)
  502. continue
  503. }
  504. return nil
  505. case 1:
  506. // escaped character state
  507. if ch >= 32 && ch <= 126 {
  508. if s.LocalPartQuotes == false && !s.isAtext(ch) {
  509. s.LocalPartQuotes = true
  510. }
  511. s.accept.WriteByte(ch)
  512. state = 0
  513. continue
  514. } else {
  515. return errors.New("non-printable character found")
  516. }
  517. }
  518. }
  519. }
  520. //Dot-string = Atom *("." Atom)
  521. func (s *Parser) dotString() error {
  522. for {
  523. if err := s.atom(); err != nil {
  524. return err
  525. }
  526. if s.ch != '.' {
  527. break
  528. }
  529. s.accept.WriteByte(s.ch)
  530. }
  531. return nil
  532. }
  533. // 1*atext
  534. func (s *Parser) atom() error {
  535. state := 0
  536. for {
  537. if state == 0 {
  538. if !s.isAtext(s.next()) {
  539. return errors.New("atom parse error")
  540. } else {
  541. s.accept.WriteByte(s.ch)
  542. state = 1
  543. continue
  544. }
  545. }
  546. if state == 1 {
  547. if !s.isAtext(s.next()) {
  548. return nil
  549. } else {
  550. s.accept.WriteByte(s.ch)
  551. }
  552. }
  553. }
  554. }
  555. /*
  556. Dot-string = Atom *("." Atom)
  557. Atom = 1*atext
  558. atext = ALPHA / DIGIT / ; Any character except controls,
  559. "!" / "#" / ; SP, and specials.
  560. "$" / "%" / ; Used for atoms
  561. "&" / "'" /
  562. "*" / "+" /
  563. "-" / "/" /
  564. "=" / "?" /
  565. "^" / "_" /
  566. "`" / "{" /
  567. "|" / "}" /
  568. "~"
  569. */
  570. func (s *Parser) isAtext(c byte) bool {
  571. if ('0' <= c && c <= '9') ||
  572. ('a' <= c && c <= 'z') ||
  573. ('A' <= c && c <= 'Z') ||
  574. c == '!' || c == '#' ||
  575. c == '$' || c == '%' ||
  576. c == '&' || c == '\'' ||
  577. c == '*' || c == '+' ||
  578. c == '-' || c == '/' ||
  579. c == '=' || c == '?' ||
  580. c == '^' || c == '_' ||
  581. c == '`' || c == '{' ||
  582. c == '|' || c == '}' ||
  583. c == '~' {
  584. return true
  585. }
  586. return false
  587. }
  588. func isLetDig(c byte) bool {
  589. if ('0' <= c && c <= '9') ||
  590. ('A' <= c && c <= 'Z') ||
  591. ('a' <= c && c <= 'z') {
  592. return true
  593. }
  594. return false
  595. }
  596. //ehlo = "EHLO" SP ( Domain / address-literal ) CRLF
  597. // Note: "HELO" is ignored here
  598. func (s *Parser) Ehlo(input []byte) (domain string, ip net.IP, err error) {
  599. s.set(input)
  600. s.next()
  601. if s.ch == ' ' {
  602. if p := s.peek(); p == '[' {
  603. err = s.addressLiteral()
  604. if err == nil {
  605. domain = s.accept.String()
  606. ip = net.ParseIP(domain)
  607. if ip == nil {
  608. err = errors.New("invalid ip")
  609. }
  610. return
  611. }
  612. } else {
  613. err = s.domain()
  614. if err == nil {
  615. domain = s.accept.String()
  616. }
  617. return
  618. }
  619. } else {
  620. err = errors.New("ehlo parse error")
  621. }
  622. return domain, ip, err
  623. }
  624. // helo = "HELO" SP Domain CRLF
  625. // Note: "HELO" is ignored here, so is the CRLF at the end
  626. func (s *Parser) Helo(input []byte) (domain string, err error) {
  627. s.set(input)
  628. s.next()
  629. if s.ch == ' ' {
  630. err = s.domain()
  631. if err == nil {
  632. domain = s.accept.String()
  633. }
  634. return
  635. } else {
  636. err = errors.New("helo parse error")
  637. }
  638. return
  639. }