├── .gitignore ├── LICENSE.txt ├── changelog.txt ├── misc ├── spampd-rh-rc-script.sh ├── spampd-sm.cfg ├── spampd.cfg └── spampd.service ├── previous-versions ├── spampd-0.0.1.pl ├── spampd-0.0.5.pl ├── spampd-1.0.2.pl ├── spampd-2.00.pl ├── spampd-2.10.pl ├── spampd-2.11.pl ├── spampd-2.12.pl ├── spampd-2.13.pl ├── spampd-2.20.pl ├── spampd-2.30.pl ├── spampd-2.32.pl ├── spampd-2.40.forceuser.pl ├── spampd-2.40.pl └── spampd.1.0.1.pl ├── readme.md ├── spampd.pl └── spampd.pod /.gitignore: -------------------------------------------------------------------------------- 1 | release 2 | .project 3 | settings.xml 4 | .settings 5 | Thumbs.db 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | SpamPD Change Log 2 | ----------------- 3 | 4 | Legend (used since v2.60): 5 | + : new feature/function 6 | * : bug/deficiency fix 7 | ~ : enhancement/non-breaking change 8 | ! : important change, change of default behavior, etc. 9 | ----------------------------------------------------------- 10 | 11 | 2.62 (9-Dec-24) 12 | 13 | * Fixed that SpamPD shares the same socket to `redist` between children. 14 | Report: https://github.com/mpaperno/spampd/issues/44 Fix: https://github.com/mpaperno/spampd/pull/45 (thanks @catap !) 15 | * Fix option names in configuration parameter validation methods (https://github.com/mpaperno/spampd/commit/29752f8) 16 | ~ Do not set up logging if only showing debug info (https://github.com/mpaperno/spampd/commit/641e6571). 17 | 18 | --- 19 | 2.61 (6-Aug-21) 20 | 21 | Bug fixes, new features, and some optimization. Thanks to Simon Matter for reporting, suggestions, and testing! 22 | 23 | * Restore syslog as default logging destination (https://github.com/mpaperno/spampd/issues/31) 24 | * Fix issues with older Perl versions (https://github.com/mpaperno/spampd/issues/30) 25 | ~ Optimize initial header processing when building message line array in process_message(). 26 | ~ Slight optimization to LMTP multi-recipient handling in process_request(). 27 | ~ Optimize how rewritten (tagged) message is saved back to temp file. 28 | + Add detection and logging of "RULESVERSION" tag with SA >= v3.4.0. 29 | + Add tracking of some per-child runtime statistics which by default are now shown in the child process names. 30 | + Add ability to provide a custom child process name template string (or not modify the child name at all). 31 | Template format documented in POD. (https://github.com/mpaperno/spampd/issues/32) 32 | + Add _SPAMPDVERSION_ as a "template tag" (macro), eg. for use in SA add_header directives. 33 | 34 | --- 35 | 2.60 (26-Jul-21) 36 | 37 | This version brings quite a few changes, though the base functionality and compatibility is unchanged 38 | (minor exceptions noted below). Testing/close observation of this new version is recommended! 39 | 40 | ~ Performance and diagnostic improvements, quicker startups, and a lot of documentation updates. 41 | + Add support for configuration files (examples included in /misc folder and in POD). 42 | + Add optional "scalable mode" using Net::Server::PreFork module (16-year TODO!). More info in POD. 43 | + Add --logfile option to control logging destination(s) (syslog, stderr, and/or file/device). 44 | + Add --logident, --logfacility options for syslog. 45 | + Add multiple levels of help, including full "man" output with optional HTML formatting. 46 | + Add --show argument for printing default option values and other debug. 47 | * Fix SpamAssassin debug logging with versions 3.1+ (output was going to stderr/wrong syslog/null). 48 | * Fix for IPv6 addresses being used on --host and --relayhost options (was not possible due to ":" check). 49 | ! SIGHUP will now reload SpamAssassin and SpamPD configuration files (and all module code), still with graceful child process shutdown. 50 | ! Use SpamAssassin::Logger module (with SA 3.1+) for all logging. This now inits logging much earlier. 51 | ! Log to stderr by default if running non-daemonized (with --nodetach). 52 | ! Child processes are now renamed to "spampd child" to distinguish them from the parent in task lists. 53 | ! Now requires Net::Server v0.89+ (though latest 2.009 is recommended). 54 | ! The --auto-whitelist option is no longer allowed with SpamAssassin v3+. 55 | ~ Improve --debug option, adding ability to specify SpamAssassin (v3.1+) debug areas (aka channels/facilities). 56 | ~ All boolean options can take 0/1 argument and be negated with "no-" prefix. 57 | ~ The --children (-c) option is now more formally named --max-servers (-mxs), but still accepted. 58 | ~ IO::Socket::UNIX and ::IP are only required if actually needed for --relaysocket / --relayhost options. 59 | ~ SpamPD can now be loaded w/out executing eg. for unit tests or other uses. Much more modular code in general. 60 | 61 | ######## 62 | 63 | 2.53 (25-Feb-19) 64 | 65 | - Fix LMTP delivery with multiple recipients (https://github.com/mpaperno/spampd/issues/23 & https://github.com/mail-in-a-box/mailinabox/issues/1523) 66 | - Fix Warning for "Use of uninitialized value in string" (https://github.com/mpaperno/spampd/issues/22) 67 | 68 | 2.52 (10-Nov-18) 69 | 70 | - Override Net::Server's HUP handling, just restart children (https://github.com/mpaperno/spampd/pull/20). 71 | - Add --version option to print information about SpamPD, Net::Server, SpamAssassin, and Perl. 72 | - Add warnings about using deprecated options. 73 | - Documentation updates and code cosmetics. 74 | 75 | 2.51 (01-May-18) 76 | 77 | - Fix listening to IP address, broken in 2.50 "Unix ports" feature. (https://github.com/mpaperno/spampd/pull/18) 78 | - Add --setsid option to start server with setsid if running in background (https://github.com/mpaperno/spampd/pull/18) 79 | 80 | 2.50 (30-Apr-18) 81 | 82 | - Replace IO::Socket::INET with IO::Socket::IP for IPv6 support (https://github.com/mpaperno/spampd/pull/9). 83 | - Unix ports (ability to listen on UNIX sockets) (https://github.com/mpaperno/spampd/pull/13). 84 | - Add X-Envelope-* headers before Received (https://github.com/mpaperno/spampd/pull/14). 85 | - Add /usr/local/bin and /usr/local/sbin to PATH (https://github.com/mpaperno/spampd/pull/17). 86 | 87 | Please refer to commit notes at (https://github.com/mpaperno/spampd/compare/2.42...2.50) for details and credits. 88 | 89 | ######## 90 | 91 | 2.42 (08-Dec-13) (experimental) 92 | 93 | - Untaint some params for compatibility with Perl 5.18. 94 | 95 | 2.41 (11-Aug-10) (experimental) 96 | 97 | - Added setting of user name at SA init time. 98 | 99 | 2.40 (10-Jan-09) (experimental) 100 | 101 | - New config option to load a specific configuration file after the default 102 | local.cf file, thereby overriding any settings therein. The new option is 103 | --saconfig=filename. Thanks to Sven Mueller for code and Bernd Zeimetz for 104 | bringing it up. (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=344373) 105 | 106 | - Integrated code by Alexander Wirt to introduce a parameter which 107 | sets a proper home directory (--homedir=path) and also cleans up the 108 | environment before backgrounding. 109 | (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=421100) 110 | * NOTE: * default homedir is /var/spool/spamassassin/spampd which needs to be 111 | writable by the user spampd is running as. Previously, some files like the 112 | auto-whitelist were written to the .spamassassin folder inside the users home 113 | directory who started spampd, typically root. 114 | 115 | - Integrated fix from Vladislav Kurz for LMTP multi-line response after DATA 116 | is sent. (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=395355) 117 | 118 | - Yet another fix for older Net::Server versions (<= 0.87) dying when logging 119 | a % character to Sys::Syslog. This also fixes the bug in 2.30 that logs "%s" 120 | instead of the actual messages on some system. 121 | 122 | - Fixed bug with temp files sticking around until spampd child exists, 123 | introduced when SA 3.0 was released 124 | (https://issues.apache.org/SpamAssassin/show_bug.cgi?id=5444). 125 | Thanks to Simon Matter for bringing this to my attention. 126 | 127 | ######## 128 | 129 | 2.30 (31-Oct-05) 130 | - Another, hopefully final, fix for the Sys::Syslog issue of % signs in the log 131 | string. Fixes possible DoS vulnerability. Thanks to Sven Mueller and Florian 132 | Weimer for the solution. 133 | - Added new options for adding X-Envelope-From and (optionally) X-Envelope-To 134 | headers to messages before SA processing. The idea is to help SA process any 135 | blacklist/whitelist to/from directives on the actual sender/recipients instead 136 | of the possibly bogus envelope headers. Use --seh or --set-envelope-headers 137 | to enable setting both headers, or use --sef or --set-envelope-from to enable 138 | only X-Envelope-From. If added, spampd attempts to remove the X-Envelope-To 139 | header after SA processing to preserve BCC recipient anonymity, but enabling 140 | this header may still expose recipient information. See man page for more 141 | details. This patch was originally submitted by Sven Mueller, was slightly 142 | modified, and the --sef option was added. 143 | 144 | ######## 145 | 146 | 2.21 (23-Oct-05) (unreleased) 147 | - fixed SA version check on alphanumeric version strings. Stops the annoying 148 | Perl warning messages in the mail log. Thanks to Sven Mueller for the fix. 149 | 150 | ######## 151 | 152 | 2.20 (05-Oct-04) 153 | - added support for SpamAssassin version 3. spampd should now support all 154 | SA versions (tested with 2.6.3 and 3.0.0). 155 | - removed --add-sc-header feature. It is now redundant with SA v2.6 ability 156 | to (almost fully) customize headers, which v3 improves on. If anyone 157 | really needs this feature, please let me know. 158 | - added --nodetach option to prevent daemon process backgrounding. Patch 159 | provided by Urban Petry. Can be useful for win32/cygwin. 160 | - if --debug is specified, Net::Server log level is increased to 4 (debug) 161 | to provide some more info in the log (can be useful for diagnosing 162 | user/permission issues). Thanks to Urban Petry for idea. 163 | - the message sender (From header) is now included in the log along with message 164 | ID, recipient, and scoring info. Thanks to Roland Koeckel for the patch. 165 | 166 | ######## 167 | 168 | 2.13 (24-Nov-03) 169 | - SA debug messages redirected from STDERR (warn) to syslog. Thanks to Roland 170 | Koeckel for the suggestion. 171 | 172 | ######## 173 | 174 | 2.12 (15-Nov-03) 175 | - fixed bug related to Sys::Syslog where we needed to escape % signs in 176 | Message IDs. Thanks to Jeffrey W. Collyer and Yann Grossel for the bug reports. 177 | - minor performance improvement in SpamPD::Client using buffered write to send 178 | message data. Thanks to Sam Horrocks for the tip. 179 | - fixed error condition when an error response ([4|5]xx) was returned after a 180 | DATA command was sent. Thanks to Rodrigo Ventura for bug reports about this. 181 | 182 | ######## 183 | 184 | 2.11 (15-Jul-03): 185 | - fix for occasional corrupted message headers which caused blank messages 186 | (seemed to have only affected certain malformed spam mail). 187 | - added --logsock option for syslog socket. Defaults to 'unix' except for 188 | HP-UX and SunOS (Solaris) which I'm told prefer 'inet'. 189 | 190 | ######## 191 | 192 | 2.10 (01-Jul-03): 193 | - added optional 'X-Spam-Checked-By: {hostname}' header, where {hostname} is, 194 | theoretically, the name of the machine doing the message scanning. New 195 | options --add-sc-header and --hostname=name control this behavior. 196 | 197 | ######## 198 | 199 | 2.00 (10-Jun-03): 200 | - major rewrite of how mail is handled internally. spampd now takes no 201 | responsibility for the mail at any point, instead acting as a transparent 202 | proxy between the originating and the destination servers. That is, the 203 | servers speak to each other through spampd so final mail delivery 204 | occurs only when the destination server acknowledges receipt of the data. 205 | Idea based on smtpprox by Bennett Todd (http://bent.latency.net/smtpprox/). 206 | Unfortunately this breaks the ability to redirect the mail based on spam 207 | score, since scoring happens after all recipients have been specified and 208 | accepted. But, it is much cleaner and safer than the previous method. 209 | 210 | - new architecture doesn't store the mail data in memory any more. Message 211 | is still written to memory before scanning by SpamAssassin, but messages 212 | larger than the --maxsize to be scanned won't eat up a bunch of memory. 213 | From smtpprox documentation by Bennet Todd: 214 | "it [spampd] stores the body of the message in an unlinked file 215 | under /tmp, which should be a tmpfs; this prevents the allocation 216 | overhead associated with large strings (often 2-3x) and ensures that 217 | space will be returned to the OS as soon as it's not needed." 218 | 219 | - as a bonus feature, LMTP is now supported by virtue of spampd's transparency. 220 | 221 | - added a timeout check around the socket operations as suggested in the 222 | Net::Server docs. Added new parameter to control this: --childtimeout=n 223 | where n is number of seconds. 224 | 225 | - added a timeout check around the message processing (spam checking) routines 226 | to guard against a SpamAssassin hang. Added new parameter to control 227 | this: --satimeout=n where n is number of seconds. If a timeout (or error) 228 | occurs while processing, the mail is still passed on unless the new --dose 229 | (die-on-sa-errors) paramater is given. 230 | 231 | - added --children=n parameter to specify how many child 232 | servers to spawn and maintain. Default is 5 children (plus 233 | one parent). 234 | 235 | - now uses Net::Server::PreForkSimple instead of PreFork. (Tried utilizing the 236 | advanced children pool features of PreFork but either couldn't figure it out 237 | or they're kinda broken. If anyone has experience here, please let me know.) 238 | 239 | - improved logging including the Message-ID, recipients, 100ths precision 240 | on spam score, processing time, and file size. Logging format now better 241 | resembles that of spamd (which hopefully means spamd log analysis tools can be 242 | made to work with spampd easily). 243 | 244 | - removed dependencies on Net::SMTP, Net::SMTP::Server::Client, and Error 245 | modules. 246 | 247 | - host/port and relay host/port can both be specified as xx.xx.xx.xx:nn in 248 | the --host and --relayhost parameters, or as individual parameters (--host, 249 | --port, --relayhost, --relayport). 250 | 251 | # The next 3 items are ideas/patches by 252 | # Kurt Andersen, 253 | # Agilent Technologies Postmaster 254 | # Global Messaging Team, Agilent Technologies 255 | 256 | - added optional support for Time::HiRes for more accurate processing time 257 | reporting in the log (automatically loaded if Time::HiRes is available). 258 | 259 | - added optional logging of which SA rules matched a message. New option is 260 | --log-rules-hit or --rh for short. 261 | 262 | - Added auto HPUX OS detection for syslog loggging 263 | "(for some reason HPUX chokes on using the 'unix' socket type)." 264 | 265 | # Thanks Kurt! 266 | 267 | - added much more verbose spampd logging when using the --debug option. 268 | 269 | - 3 parameters are now deprecated but accepted for backwards compatability: 270 | --dead-letters, --heloname, and --stop-at-threshold 271 | 272 | - added shorthand choice for some options: 273 | --aw for --auto-whitelist; --L for --local-only; --a for --tagall 274 | --u for --user; --g for --group; --p for --pid 275 | --d for --debug; --h for --help; 276 | 277 | - documentation updates 278 | 279 | - licensing change due to use of Bennet Todd's code (to GNU GPL from Perl 280 | Artistic). 281 | 282 | ######## 283 | 284 | 1.0.2 (13-Apr-03): 285 | - added 'local-only' parameter to pass on to SA which turns off all 286 | network-based tests (DNS, Razor, etc). 287 | 288 | ######## 289 | 290 | 1.0.1 (3-Feb-03): 291 | - fixed minor but substantial bug preventing child processes 292 | from exiting properly since the counter wasn't being incremented (d'oh!). 293 | Thanks to Mark Blackman for pointing this out. 294 | 295 | - fixed typo in pod docs (Thx to James Sizemore for pointing out) 296 | 297 | ######## 298 | 299 | Changes to assassind (1.0.0 initial release of spampd - May 2002): 300 | A different message rewriting method (using 301 | Mail::SpamAssassin::NoMailAudit instead of Dave Carrigan's 302 | custom headers and Mail::Audit); 303 | Adding more options for message handling, network/protocol options, 304 | some options to pass on to SpamAssassin (such as whitelist usage); 305 | More orientation to being used as a content filter for the 306 | Postfix MTA, mostly by changing some default values; 307 | Documentation changes; 308 | 309 | ## EOF ## 310 | -------------------------------------------------------------------------------- /misc/spampd-rh-rc-script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # This script starts and stops the spampd daemon 4 | # 5 | #### NOTE ##### 6 | # This is a very old and outdated example!!! 7 | # Recommend checking the Debian version of spampd.init script, 8 | # in the /debian branch of the source repository 9 | # (https://github.com/mpaperno/spampd/tree/debian). 10 | # 11 | # chkconfig: 2345 80 30 12 | # 13 | # description: spampd is a daemon process which uses SpamAssassin to check 14 | # email messages for SPAM. 15 | 16 | # Source function library. 17 | . /etc/rc.d/init.d/functions 18 | 19 | # Source networking configuration. 20 | . /etc/sysconfig/network 21 | 22 | # Check that networking is up. 23 | [ ${NETWORKING} = "no" ] && exit 0 24 | 25 | [ -f /usr/bin/spampd -o -f /usr/local/bin/spampd ] || exit 0 26 | PATH=$PATH:/usr/bin:/usr/local/bin 27 | 28 | # See how we were called. 29 | case "$1" in 30 | start) 31 | # Start daemon. 32 | echo -n "Starting spampd: " 33 | daemon spampd --port=10025 --relayhost=127.0.0.1:25 --tagall --auto-whitelist 34 | RETVAL=$? 35 | touch /var/lock/spampd 36 | echo 37 | ;; 38 | stop) 39 | # Stop daemons. 40 | echo -n "Shutting down spampd: " 41 | killproc spampd 42 | RETVAL=$? 43 | rm -f /var/lock/spampd 44 | echo 45 | ;; 46 | restart) 47 | $0 stop 48 | $0 start 49 | ;; 50 | status) 51 | status spampd 52 | ;; 53 | *) 54 | echo "Usage: $0 {start|stop|restart|status}" 55 | exit 1 56 | esac 57 | 58 | exit 0 59 | -------------------------------------------------------------------------------- /misc/spampd-sm.cfg: -------------------------------------------------------------------------------- 1 | # Configuration file example for SpamPD in "scalable mode" (see documentation for details). 2 | # The options here are meant to be in addition to/override options set in a 3 | # "main" SpamPD config file, such as the "spampd.cfg" example in this folder. 4 | # This file can simply be appended to the main one using the command line, e.g.: 5 | # spampd --config /etc/spampd.cfg --config /etc/spampd-sm.cfg 6 | 7 | # The minimum number of servers to keep running 8 | min-servers 5 9 | 10 | # The minimum number of servers to have waiting 11 | min-spare 2 12 | 13 | # The maximum number of servers to have waiting 14 | max-spare 10 15 | 16 | # The maximum number of child servers to start. 17 | max-servers 20 18 | 19 | 20 | # Passthrough tuning arguments for Net::Server::PreFork could go here. 21 | # Be sure to also uncomment the "--" if using any. 22 | # -- 23 | # check_for_dead 30 24 | # check_for_waiting 10 25 | # check_for_spawn 30 26 | # min_child_ttl 10 27 | -------------------------------------------------------------------------------- /misc/spampd.cfg: -------------------------------------------------------------------------------- 1 | # Configuration file example for SpamPD v2.6+ 2 | # One option per line. Comments (start with # or ;) and blank lines are skipped. 3 | # Using a "-" or "--" prefix on the argument names is optional. 4 | # Name/value separators can be one or more of space, tab, or = sign. 5 | # See main SpamPD documentation for full options list, file syntax, and other details. 6 | 7 | # User and Group ID to run as. SpamPD's default is "mail:mail" but typical Debian install uses "spampd:spampd". 8 | user spampd 9 | group spampd 10 | 11 | # Where to write the PID file (SpamPD user must have r/w access) 12 | pid /var/run/spampd/spampd.pid 13 | 14 | # Home directory for the SpamAssassin process (SpamPD user must have r/w access) 15 | homedir /var/cache/spampd 16 | 17 | # The IP and port to listen on 18 | host 127.0.0.1 19 | port 10025 20 | 21 | # Listen on a unix socket instead 22 | # socket /var/run/spampd/spampd.socket 23 | # socket-perms 700 24 | 25 | # The host and port to forward the connection to 26 | relayhost 127.0.0.1 27 | relayport 10026 28 | 29 | # Relay using a socket instead 30 | # relaysocket /var/run/dovecot/lmtp 31 | 32 | # How many checks can be done in parallel. 33 | # (note: this option was named "children" in SpamPD versions before 2.60; "children" is also still valid.) 34 | max-servers 3 35 | 36 | # Whether or not to tag all messages, even non-spam (0/1) 37 | tagall 1 38 | 39 | # Whether or not to do only local checks (disables any network checks like DNS blacklisting) (0/1) 40 | local-only 1 41 | 42 | # Logging destination, could be syslog (default), stderr, or a filename. 43 | # logfile /var/log/spampd.log 44 | 45 | # Syslog socket type to use when logfile = syslog. Could be any type supported by syslog(1). 46 | # logsock inet 47 | 48 | # The syslog "identity" to use (typically included in the logged details). Default is "spampd" 49 | # logident spampd 50 | 51 | # The syslog "facility" (typically the log file name in /var/log). Default is "mail" 52 | # logfacility mail 53 | 54 | # Use a spcific "user" config file to override parameters from the system-wide SpamAssassin configuration. 55 | # saconfig /etc/spampd.sa.cf 56 | 57 | # Enable logging of all SpamAssassin rules hit per scanned message. (0/1) 58 | # log-rules-hit 1 59 | 60 | # Add X-Envelope-From header to messages (if not already present). (0/1) 61 | # set-envelope-from 1 62 | 63 | # Debug logging options. The default value of 0 will disable it. 64 | # A value of 1 or "all" will enable very verbose logging from SpamAssassin and SpamPD. 65 | # A value of "spampd" will enable SpamPD debug only. 66 | # Other values correspond to SpamAssassin's logging categories and will also enable SpamPD debug. 67 | # debug 1 68 | # debug spampd 69 | # debug config,rules 70 | 71 | # Passthrough arguments for Net::Server[::PreFork[Simple]] could go here (see documentation for details). 72 | # Be sure to also uncomment the "--" if using any. 73 | # -- 74 | # cidr_allow 127.0.0.1/32 75 | # cidr_allow 192.168.1.0/24 76 | # cidr_deny 192.168.1.4/30 77 | # reverse_lookups 1 78 | # allow localhost 79 | # check_for_dead 30 80 | # chroot 81 | -------------------------------------------------------------------------------- /misc/spampd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Spam Proxy Daemon 3 | After=syslog.target network.target 4 | 5 | [Service] 6 | ExecStart=/usr/sbin/spampd --config /etc/spampd.cfg --pid /run/spampd.pid --nodetach 7 | ExecReload=/usr/bin/kill -HUP $MAINPID 8 | KillMode=mixed 9 | KillSignal=SIGQUIT 10 | TimeoutStopSec=30 11 | Restart=on-failure 12 | # To run as a forking server, uncomment below and remove the "--nodetach" option 13 | # from the command line above. You may need/want to add "--setsid" option instead. 14 | #Type=forking 15 | #PIDFile=/run/spampd.pid 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /previous-versions/spampd-0.0.1.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | ############################################################ 4 | # This code is a merging of spamd (copyright 2001 by Craig Hughes) 5 | # and spamproxyd by Ian R. Justman. 6 | # Like spamproxyd, it is written with Postfix "advanced" content 7 | # filtering in mind. See FILTER_README in the Postfix distribution 8 | # for more information on how to set this up. 9 | # 10 | # The primary difference between spamproxyd and spampd is that 11 | # spampd acutally tags the spams and sends them on, even making 12 | # use of the auto-whitelist feature (and soon the SQL lookups 13 | # based on the recipient email address, maybe). 14 | # 15 | # The primary difference between spamd and spampd is that spampd 16 | # talks SMTP protocol for its I/O stream (via Net::SMTP::Server 17 | # and Mail::SpamAssassin::SMTP::SmartHost). 18 | # 19 | # WARNING: Use at your own risk. Basically I have no idea what 20 | # I'm doing with the process forking stuff, I just copied it and 21 | # messed around until it worked (for me, YMMV). Also the demonizing 22 | # stuff seems screwy when you go to kill the daemon (at least via inet.d 23 | # script), but it does exit and clean up so no harm seems to be done. 24 | # 25 | # Development/production environment is RH Linux 7.x on various x86 hardware. 26 | # 27 | # BUG: for some reason the log and warn (if -D) messages don't print during 28 | # SIGTERM or when a child dies (after processing it's allotted # of msgs). 29 | # I have no idea why (see above) 30 | # 31 | # spampd is licensed for use under the terms of the Perl Artistic License 32 | # 33 | ############################################################ 34 | 35 | # use lib '../lib'; # added by jm for use inside the distro 36 | use strict; 37 | # use Socket; 38 | use Carp; 39 | use Net::SMTP::Server; 40 | use Net::SMTP::Server::Client; 41 | use Mail::SpamAssassin; 42 | use Mail::SpamAssassin::NoMailAudit; 43 | use Mail::SpamAssassin::SMTP::SmartHost; 44 | use Net::DNS; 45 | use Sys::Syslog qw(:DEFAULT setlogsock); 46 | use POSIX qw(setsid); 47 | use Getopt::Std; 48 | use POSIX ":sys_wait_h"; 49 | 50 | my %resphash = ( 51 | EX_OK => 0, # no problems 52 | EX_USAGE => 64, # command line usage error 53 | EX_DATAERR => 65, # data format error 54 | EX_NOINPUT => 66, # cannot open input 55 | EX_NOUSER => 67, # addressee unknown 56 | EX_NOHOST => 68, # host name unknown 57 | EX_UNAVAILABLE => 69, # service unavailable 58 | EX_SOFTWARE => 70, # internal software error 59 | EX_OSERR => 71, # system error (e.g., can't fork) 60 | EX_OSFILE => 72, # critical OS file missing 61 | EX_CANTCREAT => 73, # can't create (user) output file 62 | EX_IOERR => 74, # input/output error 63 | EX_TEMPFAIL => 75, # temp failure; user is invited to retry 64 | EX_PROTOCOL => 76, # remote error in protocol 65 | EX_NOPERM => 77, # permission denied 66 | EX_CONFIG => 78, # configuration error 67 | ); 68 | 69 | sub usage 70 | { 71 | warn <new({ 156 | dont_copy_prefs => $dontcopy, 157 | local_tests_only => $opt_L, 158 | debug => $opt_D, 159 | paranoid => ($opt_P || 0), 160 | }); 161 | 162 | $opt_w and eval 163 | { 164 | require Mail::SpamAssassin::DBBasedAddrList; 165 | 166 | # create a factory for the persistent address list 167 | my $addrlistfactory = Mail::SpamAssassin::DBBasedAddrList->new(); 168 | $spamtest->set_persistent_address_list_factory ($addrlistfactory); 169 | }; 170 | 171 | sub logmsg; # forward declaration 172 | 173 | setlogsock('unix'); 174 | 175 | # Use Net::SMTP::Server here to talk regular SMTP 176 | my $server = new Net::SMTP::Server($addr, $port) || 177 | die "Unable to create server: $! : $addr, $port\n"; 178 | 179 | # support non-root use (after we bind to the port) 180 | my $setuid_to_user = 0; 181 | if ($opt_u) { 182 | my $uuid = getpwnam($opt_u); 183 | if (!defined $uuid || $uuid == 0) { 184 | die "fatal: cannot run as nonexistent user or root with -u option\n"; 185 | } 186 | $> = $uuid; # effective uid 187 | $< = $uuid; # real uid. we now cannot setuid anymore 188 | if ($> != $uuid) { 189 | die "fatal: setuid to uid $uuid failed\n"; 190 | } 191 | } 192 | 193 | $spamtest->compile_now(); # ensure all modules etc. are loaded 194 | $/ = "\n"; # argh, Razor resets this! Bad Razor! 195 | 196 | $opt_d and daemonize(); 197 | 198 | my $current_user; 199 | 200 | if ($opt_D) { 201 | warn "server started on port $port\n"; 202 | warn "server pid: $$\n"; 203 | } 204 | logmsg "server started on $addr:$port; server pid: $$\n"; 205 | 206 | # Ian R. Justman writes in spamproxyd: 207 | # This is the preforking and option-parsiong section taken from the MSDW 208 | # smtpproxy code by Bennett Todd. Any comments from that code are not my 209 | # own comments (marked with "[MSDW]") unless otherwise noted. 210 | # 211 | # Depending on your platform, you may need his patch which uses 212 | # IPC/semaphores to get information which may be required to allow two 213 | # simultaneous instances to accept() a connection, which can be obtained at 214 | # http://bent.latency.net/smtpprox/smtpprox-semaphore-patch. It is best to 215 | # apply the patch to the original script, then port it to this one. 216 | # 217 | # --irj 218 | 219 | # [MSDW] 220 | # This should allow a kill on the parent to also blow away the 221 | # children, I hope 222 | my %children; 223 | use vars qw($please_die); 224 | $please_die = 0; 225 | $SIG{INT} = sub { $please_die = 1; }; 226 | $SIG{TERM} = sub { $please_die = 1; }; # logmsg "server killed by SIGTERM, shutting down"; 227 | 228 | # [MSDW] 229 | # This sets up the parent process 230 | 231 | PARENT: while (1) { 232 | while (scalar(keys %children) >= $children) { 233 | my $child = wait; 234 | delete $children{$child} if exists $children{$child}; 235 | if ($please_die) { kill 15, keys %children; exit 0; } 236 | } 237 | my $pid = fork; 238 | die "$0: fork failed: $!\n" unless defined $pid; 239 | last PARENT if $pid == 0; 240 | $children{$pid} = 1; 241 | select(undef, undef, undef, 0.1); 242 | if ($please_die) { kill 15, keys %children; exit 0; } 243 | } 244 | 245 | # [MSDW] 246 | # This block is a child service daemon. It inherited the bound 247 | # socket created by SMTP::Server->new, it will service a random 248 | # number of connection requests in [minperchild..maxperchild] then 249 | # exit 250 | 251 | my $lives = $minperchild + (rand($maxperchild - $minperchild)); 252 | 253 | while(my $conn = $server->accept()) { 254 | 255 | my $client = new Net::SMTP::Server::Client($conn) || 256 | next; 257 | 258 | my $start = time; 259 | 260 | # [MSDW] 261 | # Process the client. This command will block until 262 | # the connecting client completes the SMTP transaction. 263 | $client->process || next; 264 | 265 | # we'll have to revisit this later 266 | # if ($opt_q) { 267 | # handle_user_sql($1); 268 | # } 269 | 270 | my $resp = "EX_OK"; 271 | 272 | # Now read in message 273 | my $message = $client->{MSG}; 274 | my @msglines = split ("\r\n", $message); 275 | my $arraycont = @msglines; for(0..$arraycont) { $msglines[$_] .= "\r\n"; } 276 | # Audit the message 277 | my $mail = Mail::SpamAssassin::NoMailAudit->new ( 278 | data => \@msglines, 279 | add_From_line => $opt_F 280 | ); 281 | 282 | # Check spamminess and rewrite mail if high spam factor or option -a (tag All) 283 | my $status = $spamtest->check($mail); 284 | if ( $status->is_spam || $opt_a ) { 285 | $status->rewrite_mail; 286 | } 287 | 288 | # Build the message to send back 289 | my $msg_resp = join '',$mail->header,"\n",@{$mail->body}; 290 | 291 | # Relay the (rewritten) message through perl SmartHost module 292 | my $relay = new Mail::SpamAssassin::SMTP::SmartHost($client->{FROM}, 293 | $client->{TO}, 294 | $msg_resp, 295 | "$smarthost", 296 | "$myhelo"); 297 | 298 | # Log what we did, FWIW 299 | my $was_it_spam; 300 | if($status->is_spam) { $was_it_spam = 'identified spam'; } else { $was_it_spam = 'clean message'; } 301 | my $msg_score = int($status->get_hits); 302 | my $msg_threshold = int($status->get_required_hits); 303 | #$current_user ||= '(unknown)'; 304 | logmsg "$was_it_spam ($msg_score/$msg_threshold) in ". 305 | sprintf("%3d", time - $start) ." seconds.\n"; 306 | 307 | $status->finish(); # added by jm to allow GC'ing 308 | 309 | # Zap this instance if this child's processing limit has been reached. 310 | # --irj 311 | delete $server->{"s"}; 312 | if ($lives-- <= 0) { 313 | if ($opt_D) { 314 | warn "killing child process\n"; 315 | } 316 | exit 0; 317 | } 318 | } 319 | 320 | sub handle_user_sql 321 | { 322 | $current_user = shift; 323 | $spamtest->load_scoreonly_sql ($current_user); 324 | return 1; 325 | } 326 | 327 | sub logmsg 328 | { 329 | openlog('spamd','cons,pid',$log_facility); 330 | syslog('info',"@_"); 331 | if ($opt_D) { warn "logmsg: @_\n"; } 332 | } 333 | 334 | sub kill_handler 335 | { 336 | my ($sig) = @_; 337 | logmsg "server killed by SIG$sig, shutting down"; 338 | $please_die = 1; 339 | return 1; 340 | #close Server; 341 | #exit 0; 342 | } 343 | 344 | use POSIX 'setsid'; 345 | sub daemonize 346 | { 347 | chdir '/' or die "Can't chdir to '/': $!"; 348 | open STDIN,'/dev/null' or die "Can't read '/dev/null': $!"; 349 | open STDOUT,'>/dev/null' or die "Can't write '/dev/null': $!"; 350 | defined(my $pid=fork) or die "Can't fork: $!"; 351 | exit if $pid; 352 | setsid or die "Can't start new session: $!"; 353 | open STDERR,'>&STDOUT' or die "Can't duplicate stdout: $!"; 354 | } 355 | 356 | =head1 NAME 357 | 358 | spampd - daemonized version of spamassassin with SMTP IO interface 359 | 360 | =head1 SYNOPSIS 361 | 362 | spampd [options] 363 | 364 | =head1 OPTIONS 365 | 366 | =over 367 | 368 | =item B<-w> 369 | 370 | Use auto-whitelists. These will automatically create a list of 371 | senders whose messages are to be considered non-spam by monitoring the total 372 | number of received messages which weren't tagged as spam from that sender. 373 | Once a threshold is exceeded, further messages from that sender will be given a 374 | non-spam bonus (in case you correspond with people who occasionally swear in 375 | their emails). 376 | 377 | =item B<-a> 378 | 379 | Tag All messages with SpamAssassin X-Spam-Status header, even if non spam. Default 380 | is to tag spam only. 381 | 382 | =item B<-d> 383 | 384 | Detach from starting process and run in background (daemonize). 385 | 386 | =item B<-h> 387 | 388 | Print a brief help message, then exit without further action. 389 | 390 | =item B<-i> I 391 | 392 | Tells spamd to listen on the specified IP address [defaults to 127.0.0.1]. Use 393 | 0.0.0.0 to listen on all interfaces. 394 | 395 | =item B<-p> I 396 | 397 | Optionally specifies the port number for the server to listen on. 398 | 399 | =item B<-t> I 400 | 401 | Use specified IP address as relay (To) host (default: 127.0.0.1) 402 | 403 | =item B<-v> I 404 | 405 | Use specified port on the relay host specified with -t (default: 10026) 406 | 407 | =item B<-g> I 408 | 409 | Use specified hostname in the SMTP HELO greeting to the relay host (default: spamfilter.localdomain) 410 | 411 | =item B<-q> 412 | 413 | Turn on SQL lookups even when per-user config files have been disabled 414 | with B<-x>. this is useful for spamd hosts which don't have user's 415 | home directories but do want to load user preferences from an SQL 416 | database. 417 | 418 | =item B<-s> I 419 | 420 | Specify the syslog facility to use (default: mail). 421 | 422 | =item B<-u> I 423 | 424 | Run as the named user. The alternative, default behaviour is to setuid() to 425 | the user running C, if C is running as root. 426 | 427 | =item B<-D> 428 | 429 | Print debugging messages 430 | 431 | =item B<-C> 432 | 433 | Number of child processes to create (Default: 4) 434 | 435 | =item B<-m> 436 | 437 | Minumum number of connections to handle per child before exiting (Default: 5) 438 | 439 | =item B<-M> 440 | 441 | Maximum number of connections to handle per child before exiting (Default: 10) 442 | 443 | =item B<-L> 444 | 445 | Perform only local tests on all mail. In other words, skip DNS and other 446 | network tests. Works the same as the C<-L> flag to C. 447 | 448 | =item B<-P> 449 | 450 | Die on user errors (for the user passed from spamc) instead of falling back to 451 | user I and using the default configuration. 452 | 453 | =item B<-F> I<0 | 1> 454 | 455 | Ensure that the output email message either always starts with a 'From ' line 456 | (I<1>) for UNIX mbox format, or ensure that this line is stripped from the 457 | output (I<0>). (default: 1) 458 | 459 | =back 460 | 461 | =head1 DESCRIPTION 462 | 463 | The purpose of this program is to provide a daemonized version of the 464 | spamassassin executable. The goal is improving throughput performance for 465 | automated mail checking. 466 | 467 | This version uses SMTP as the I/O transport. It is inteded to be used as a 468 | Postfix content_filter or other transport agent. 469 | 470 | This code is a merging of spamd (copyright 2001 by Craig Hughes) 471 | and spamproxyd by Ian R. Justman. 472 | Like spamproxyd, it is written with Postfix "advanced" content 473 | filtering in mind. See FILTER_README in the Postfix distribution 474 | for more information on how to set this up. 475 | 476 | The primary difference between spamproxyd and spampd is that 477 | spampd acutally tags the spams and sends them on, even making 478 | use of the auto-whitelist feature (and soon the SQL lookups 479 | based on the recipient email address, maybe). 480 | 481 | The primary difference between spamd and spampd is that spampd 482 | talks SMTP protocol for its I/O stream (via Net::SMTP::Server 483 | and Mail::SpamAssassin::SMTP::SmartHost). 484 | 485 | WARNING: Use at your own risk. Basically I have no idea what 486 | I'm doing with the process forking stuff, I just copied it and 487 | messed around until it worked (for me, YMMV). Also the demonizing 488 | stuff seems screwy when you go to kill the daemon (at least via inet.d 489 | script), but it does exit and clean up so no harm seems to be done. 490 | 491 | Development/production environment is RH Linux 7.x on various x86 hardware. 492 | 493 | BUG: for some reason the log and warn (if -D) messages don't print during 494 | SIGTERM or when a child dies (after processing it's allotted # of msgs). 495 | I have no idea why (see above) 496 | 497 | =head1 SEE ALSO 498 | 499 | spamassassin(1) 500 | Mail::SpamAssassin(3) 501 | 502 | =head1 AUTHOR 503 | 504 | Maxim Paperno EMPaperno@worldDesign.comE 505 | 506 | =head1 CREDITS 507 | 508 | Justin Mason and Craig Hughes for B 509 | and B 510 | 511 | Ian R. Justman for his B implementation 512 | 513 | Habeeb J. "MacGyver" Dihu for his B code 514 | 515 | Bennett Todd for the perforking code and option-parsing code from his 516 | pacakge, smtpproxy (used via spamproxyd code) 517 | 518 | =head1 PREREQUISITES 519 | 520 | C 521 | C 522 | 523 | =cut 524 | -------------------------------------------------------------------------------- /previous-versions/spampd-0.0.5.pl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpaperno/spampd/b023c3b5ca372060904faa7cc051902430d05a02/previous-versions/spampd-0.0.5.pl -------------------------------------------------------------------------------- /previous-versions/spampd-1.0.2.pl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpaperno/spampd/b023c3b5ca372060904faa7cc051902430d05a02/previous-versions/spampd-1.0.2.pl -------------------------------------------------------------------------------- /previous-versions/spampd-2.00.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | ###################### 4 | # SpamPD - spam proxy daemon 5 | # 6 | # v2.00 - 8-June-03 7 | # v1.0.2 - 13-Apr-03 8 | # v1.0.1 - 3-Feb-03 9 | # v1.0.0 - May 2002 10 | # 11 | # spampd is Copyright (c) 2002 by World Design Group and Maxim Paperno 12 | # (see http://www.WorldDesign.com/index.cfm/rd/mta/spampd.htm) 13 | # 14 | # Written and maintained by Maxim Paperno (MPaperno@WorldDesign.com) 15 | # 16 | # This program is free software; you can redistribute it and/or modify 17 | # it under the terms of the GNU General Public License as published by 18 | # the Free Software Foundation; either version 2 of the License, or 19 | # (at your option) any later version. 20 | # 21 | # This program is distributed in the hope that it will be useful, 22 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | # GNU General Public License for more details. 25 | # 26 | # The GNU GPL can be found at http://www.fsf.org/copyleft/gpl.html 27 | # 28 | # spampd v2 uses two Perl modules by Bennett Todd and Copyright (C) 2001 Morgan 29 | # Stanley Dean Witter. These are also distributed under the GNU GPL (see 30 | # module code for more details). Both modules have been slightly modified 31 | # from the originals and are included in this file under new names. 32 | # 33 | # spampd v1 was based on code by Dave Carrigan named assassind. Trace amounts 34 | # of his code or documentation may still remain. Thanks to him for the 35 | # original inspiration and code. (see http://www.rudedog.org/assassind/) 36 | # 37 | ###################### 38 | 39 | 40 | ################################################################################ 41 | package SpamPD::Server; 42 | 43 | # Originally known as MSDW::SMTP::Server 44 | # 45 | # This code is Copyright (C) 2001 Morgan Stanley Dean Witter, and 46 | # is distributed according to the terms of the GNU Public License 47 | # as found at . 48 | # 49 | # Modified for use in SpamPD by Maxim Paperno (June, 2003) 50 | # 51 | # This program is distributed in the hope that it will be useful, 52 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 53 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 54 | # GNU General Public License for more details. 55 | # 56 | # Written by Bennett Todd 57 | 58 | # =item DESCRIPTION 59 | # 60 | # This server simply gathers the SMTP acquired information (envelope 61 | # sender and recipient, and data) into unparsed memory buffers (or a 62 | # file for the data), and returns control to the caller to explicitly 63 | # acknowlege each command or request. Since acknowlegement or failure 64 | # are driven explicitly from the caller, this module can be used to 65 | # create a robust SMTP content scanning proxy, transparent or not as 66 | # desired. 67 | # 68 | # =cut 69 | 70 | use strict; 71 | use IO::File; 72 | #use IO::Socket; 73 | 74 | # =item new(interface => $interface, port => $port); 75 | 76 | # The #interface and port to listen on must be specified. The interface 77 | # must be a valid numeric IP address (0.0.0.0 to listen on all 78 | # interfaces, as usual); the port must be numeric. If this call 79 | # succeeds, it returns a server structure with an open 80 | # IO::Socket::INET in it, ready to listen on. If it fails it dies, so 81 | # if you want anything other than an exit with an explanatory error 82 | # message, wrap the constructor call in an eval block and pull the 83 | # error out of $@ as usual. This is also the case for all other 84 | # methods; they succeed or they die. 85 | # 86 | # =cut 87 | 88 | sub new { 89 | 90 | # This now emulates Net::SMTP::Server::Client for use with Net::Server which 91 | # passes an already open socket. 92 | 93 | my($this, $socket) = @_; 94 | 95 | my $class = ref($this) || $this; 96 | my $self = {}; 97 | $self->{sock} = $socket; 98 | 99 | bless($self, $class); 100 | 101 | die "$0: socket bind failure: $!\n" unless defined $self->{sock}; 102 | $self->{state} = 'just bound'; 103 | return $self; 104 | 105 | # Original code, removed by MP for spampd use 106 | # 107 | # my ($this, @opts) = @_; 108 | # my $class = ref($this) || $this; 109 | # my $self = bless { @opts }, $class; 110 | # $self->{sock} = IO::Socket::INET->new( 111 | # LocalAddr => $self->{interface}, 112 | # LocalPort => $self->{port}, 113 | # Proto => 'tcp', 114 | # Type => SOCK_STREAM, 115 | # Listen => 65536, 116 | # Reuse => 1, 117 | # ); 118 | # die "$0: socket bind failure: $!\n" unless defined $self->{sock}; 119 | # $self->{state} = 'just bound', 120 | # return $self; 121 | 122 | } 123 | 124 | # =item accept([debug => FD]); 125 | # 126 | # accept takes optional args and returns nothing. If an error occurs 127 | # it dies, otherwise it returns when a client connects to this server. 128 | # This is factored out as a separate entry point to allow preforking 129 | # (e.g. Apache-style) or fork-per-client strategies to be implemented 130 | # on the common protocol core. If a filehandle is passed for debugging 131 | # it will receive a complete trace of the entire SMTP dialogue, data 132 | # and all. Note that nothing in this module sends anything to the 133 | # client, including the initial login banner; all such backtalk must 134 | # come from the calling program. 135 | # 136 | # =cut 137 | 138 | # sub accept { 139 | # my ($self, @opts) = @_; 140 | # %$self = (%$self, @opts); 141 | # #($self->{"s"}, $self->{peeraddr}) = $self->{sock}->accept 142 | # $self->{"s"} = $self->{sock} 143 | # or die "$0: accept failure: $!\n"; 144 | # $self->{state} = ' accepted'; 145 | # } 146 | 147 | 148 | # =item chat; 149 | # 150 | # The chat method carries the SMTP dialogue up to the point where any 151 | # acknowlegement must be made. If chat returns true, then its return 152 | # value is the previous SMTP command. If the return value begins with 153 | # 'mail' (case insensitive), then the attribute 'from' has been filled 154 | # in, and may be checked; if the return value begins with 'rcpt' then 155 | # both from and to have been been filled in with scalars, and should 156 | # be checked, then either 'ok' or 'fail' should be called to accept 157 | # or reject the given sender/recipient pair. If the return value is 158 | # 'data', then the attributes from and to are populated; in this case, 159 | # the 'to' attribute is a reference to an anonymous array containing 160 | # all the recipients for this data. If the return value is '.', then 161 | # the 'data' attribute (which may be pre-populated in the "new" or 162 | # "accept" methods if desired) is a reference to a filehandle; if it's 163 | # created automatically by this module it will point to an unlinked 164 | # tmp file in /tmp. If chat returns false, the SMTP dialogue has been 165 | # completed and the socket closed; this server is ready to exit or to 166 | # accept again, as appropriate for the server style. 167 | # 168 | # The return value from chat is also remembered inside the server 169 | # structure in the "state" attribute. 170 | # 171 | # =cut 172 | 173 | sub chat { 174 | my ($self) = @_; 175 | local(*_); 176 | if ($self->{state} !~ /^data/i) { 177 | return 0 unless defined($_ = $self->_getline); 178 | s/[\r\n]*$//; 179 | $self->{state} = $_; 180 | if (s/^helo\s+//i) { 181 | s/\s*$//;s/\s+/ /g; 182 | $self->{helo} = $_; 183 | } elsif (s/^rset\s*//i) { 184 | delete $self->{to}; 185 | delete $self->{data}; 186 | delete $self->{recipients}; 187 | } elsif (s/^mail\s+from:\s*//i) { 188 | delete $self->{to}; 189 | delete $self->{data}; 190 | delete $self->{recipients}; 191 | s/\s*$//; 192 | $self->{from} = $_; 193 | } elsif (s/^rcpt\s+to:\s*//i) { 194 | s/\s*$//; s/\s+/ /g; 195 | $self->{to} = $_; 196 | push @{$self->{recipients}}, $_; 197 | } elsif (/^data/i) { 198 | $self->{to} = $self->{recipients}; 199 | } 200 | } else { 201 | if (defined($self->{data})) { 202 | $self->{data}->seek(0, 0); 203 | $self->{data}->truncate(0); 204 | # $self->{data} = undef; 205 | } else { 206 | $self->{data} = IO::File->new_tmpfile; 207 | # $self->{data} = undef; 208 | } 209 | while (defined($_ = $self->_getline)) { 210 | if ($_ eq ".\r\n") { 211 | $self->{data}->seek(0,0); 212 | return $self->{state} = '.'; 213 | } 214 | s/^\.\./\./; 215 | $self->{data}->print($_) or die "$0: write error saving data\n"; 216 | # $self->{data} .= $_; 217 | } 218 | return(0); 219 | } 220 | return $self->{state}; 221 | } 222 | 223 | # =item ok([message]); 224 | # 225 | # Approves of the data given to date, either the recipient or the 226 | # data, in the context of the sender [and, for data, recipients] 227 | # already given and available as attributes. If a message is given, it 228 | # will be sent instead of the internal default. 229 | # 230 | # =cut 231 | 232 | sub ok { 233 | my ($self, @msg) = @_; 234 | @msg = ("250 ok.") unless @msg; 235 | $self->_print("@msg\r\n") or 236 | die "$0: write error acknowledging $self->{state}: $!\n"; 237 | } 238 | 239 | # =item fail([message]); 240 | # 241 | # Rejects the current info; if processing from, rejects the sender; if 242 | # processing 'to', rejects the current recipient; if processing data, 243 | # rejects the entire message. If a message is specified it means the 244 | # exact same thing as "ok" --- simply send that message to the sender. 245 | # 246 | # =cut 247 | 248 | sub fail { 249 | my ($self, @msg) = @_; 250 | @msg = ("550 no.") unless @msg; 251 | $self->_print("@msg\r\n") or 252 | die "$0: write error acknowledging $self->{state}: $!\n"; 253 | } 254 | 255 | # utility functions 256 | 257 | sub _getline { 258 | my ($self) = @_; 259 | local ($/) = "\r\n"; 260 | my $tmp = $self->{sock}->getline; 261 | if ( defined $self->{debug} ) { 262 | $self->{debug}->print($tmp) if ($tmp); 263 | } 264 | return $tmp; 265 | } 266 | 267 | sub _print { 268 | my ($self, @msg) = @_; 269 | $self->{debug}->print(@msg) if defined $self->{debug}; 270 | $self->{sock}->print(@msg); 271 | } 272 | 273 | 1; 274 | 275 | ################################################################################ 276 | package SpamPD::Client; 277 | 278 | # Originally known as MSDW::SMTP::Client 279 | # 280 | # This code is Copyright (C) 2001 Morgan Stanley Dean Witter, and 281 | # is distributed according to the terms of the GNU Public License 282 | # as found at . 283 | # 284 | # Modified for use in SpamPD by Maxim Paperno (June, 2003) 285 | # 286 | # This program is distributed in the hope that it will be useful, 287 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 288 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 289 | # GNU General Public License for more details. 290 | # 291 | # Written by Bennett Todd 292 | 293 | # =head1 DESCRIPTION 294 | # 295 | # MSDW::SMTP::Client provides a very lean SMTP client implementation; 296 | # the only protocol-specific knowlege it has is the structure of SMTP 297 | # multiline responses. All specifics lie in the hands of the calling 298 | # program; this makes it appropriate for a semi-transparent SMTP 299 | # proxy, passing commands between a talker and a listener. 300 | # 301 | # =cut 302 | 303 | use strict; 304 | use IO::Socket; 305 | 306 | # =item new(interface => $interface, port => $port[, timeout = 300]); 307 | # 308 | # The interface and port to talk to must be specified. The interface 309 | # must be a valid numeric IP address; the port must be numeric. If 310 | # this call succeeds, it returns a client structure with an open 311 | # IO::Socket::INET in it, ready to talk to. If it fails it dies, 312 | # so if you want anything other than an exit with an explanatory 313 | # error message, wrap the constructor call in an eval block and pull 314 | # the error out of $@ as usual. This is also the case for all other 315 | # methods; they succeed or they die. The timeout parameter is passed 316 | # on into the IO::Socket::INET constructor. 317 | # 318 | # =cut 319 | 320 | sub new { 321 | my ($this, @opts) = @_; 322 | my $class = ref($this) || $this; 323 | my $self = bless { timeout => 300, @opts }, $class; 324 | $self->{sock} = IO::Socket::INET->new( 325 | PeerAddr => $self->{interface}, 326 | PeerPort => $self->{port}, 327 | Timeout => $self->{timeout}, 328 | Proto => 'tcp', 329 | Type => SOCK_STREAM, 330 | ); 331 | die "$0: socket connect failure: $!\n" unless defined $self->{sock}; 332 | return $self; 333 | } 334 | 335 | # =item hear 336 | # 337 | # hear collects a complete SMTP response and returns it with trailing 338 | # CRLF removed; for multi-line responses, intermediate CRLFs are left 339 | # intact. Returns undef if EOF is seen before a complete reply is 340 | # collected. 341 | # 342 | # =cut 343 | 344 | sub hear { 345 | my ($self) = @_; 346 | my ($tmp, $reply); 347 | return undef unless $tmp = $self->{sock}->getline; 348 | while ($tmp =~ /^\d{3}-/) { 349 | $reply .= $tmp; 350 | return undef unless $tmp = $self->{sock}->getline; 351 | } 352 | $reply .= $tmp; 353 | $reply =~ s/\r\n$//; 354 | return $reply; 355 | } 356 | 357 | # =item say("command text") 358 | # 359 | # say sends an SMTP command, appending CRLF. 360 | # 361 | # =cut 362 | 363 | sub say { 364 | my ($self, @msg) = @_; 365 | return unless @msg; 366 | $self->{sock}->print("@msg", "\r\n") or die "$0: write error: $!"; 367 | } 368 | 369 | # =item yammer(FILEHANDLE) 370 | # 371 | # yammer takes a filehandle (which should be positioned at the 372 | # beginning of the file, remember to $fh->seek(0,0) if you've just 373 | # written it) and sends its contents as the contents of DATA. This 374 | # should only be invoked after a $client->say("data") and a 375 | # $client->hear to collect the reply to the data command. It will send 376 | # the trailing "." as well. It will perform leading-dot-doubling in 377 | # accordance with the SMTP protocol spec, where "leading dot" is 378 | # defined in terms of CR-LF terminated lines --- i.e. the data should 379 | # contain CR-LF data without the leading-dot-quoting. The filehandle 380 | # will be left at EOF. 381 | # 382 | # =cut 383 | 384 | sub yammer { 385 | my ($self, $fh) = (@_); 386 | local (*_); 387 | local ($/) = "\r\n"; 388 | while (<$fh>) { 389 | s/^\./../; 390 | $self->{sock}->print($_) or die "$0: write error: $!\n"; 391 | } 392 | $self->{sock}->print(".\r\n") or die "$0: write error: $!\n"; 393 | } 394 | 395 | 1; 396 | 397 | 398 | ################################################################################ 399 | package SpamPD; 400 | 401 | use strict; 402 | use Net::Server::PreFork; 403 | use IO::File; 404 | use Getopt::Long; 405 | # use Net::SMTP; 406 | # use Net::SMTP::Server::Client; 407 | use Mail::SpamAssassin; 408 | use Mail::SpamAssassin::NoMailAudit; 409 | # use Error qw(:try); 410 | 411 | BEGIN { 412 | import SpamPD::Server; 413 | import SpamPD::Client; 414 | } 415 | 416 | use vars qw(@ISA $VERSION); 417 | our @ISA = qw(Net::Server::PreFork); 418 | our $VERSION = '2.00'; 419 | 420 | sub process_message { 421 | my ($self, $fh) = @_; 422 | 423 | my $start = time; 424 | 425 | # this gets info about the message file 426 | (my $dev,my $ino,my $mode,my $nlink,my $uid, 427 | my $gid,my $rdev,my $size, 428 | my $atime,my $mtime,my $ctime, 429 | my $blksize,my $blocks) = $fh->stat or die "Can't stat mail file: $!"; 430 | 431 | # Only process message under --maxsize KB 432 | if ( $size < ($self->{spampd}->{maxsize} * 1024) ) { 433 | 434 | # read message into array of lines to feed to SA 435 | # notes in the SA::NoMailAudit code indicate it should take a 436 | # filehandle... but that doesn't seem to work 437 | my(@msglines); 438 | $fh->seek(0,0) or die "Can't rewind message file: $!"; 439 | while (<$fh>) { push(@msglines,$_); } 440 | 441 | # Audit the message 442 | my $mail = Mail::SpamAssassin::NoMailAudit->new ( 443 | data => \@msglines 444 | ); 445 | 446 | # use the assassin object created during startup 447 | my $assassin = $self->{spampd}->{assassin}; 448 | 449 | # Check spamminess 450 | my $status = $assassin->check($mail); 451 | 452 | # Rewrite mail if high spam factor or option --tagall 453 | if ( $status->is_spam || $self->{spampd}->{tagall} ) { 454 | $status->rewrite_mail; 455 | 456 | # Build the new message to relay 457 | my $msg_resp = join '',$mail->header,"\r\n",@{$mail->body}; 458 | my @resplines = split(/\r?\n/, $msg_resp); 459 | my $arraycont = @resplines; 460 | $fh->seek(0,0) or die "Can't rewind message file: $!"; 461 | $fh->truncate(0) or die "Can't truncate message file: $!"; 462 | for (0..$arraycont) { $fh->print($resplines[$_] . "\r\n"); } 463 | 464 | } 465 | 466 | # Log what we did 467 | my $was_it_spam = 'clean message'; 468 | if($status->is_spam) { $was_it_spam = 'identified spam'; } 469 | my $msg_score = sprintf("%.1f",$status->get_hits); 470 | my $msg_threshold = sprintf("%.1f",$status->get_required_hits); 471 | $self->log(2, "$was_it_spam ($msg_score/$msg_threshold) in ". 472 | sprintf("%.1f", time - $start) ." seconds."); 473 | 474 | $status->finish(); 475 | 476 | } else { 477 | 478 | $self->log(2, "Scanning skipped due to size (". $size / 1024 ."KB)"); 479 | 480 | } 481 | 482 | return 1; 483 | 484 | } 485 | 486 | sub process_request { 487 | my $self = shift; 488 | my $msg; 489 | 490 | eval { 491 | 492 | local $SIG{ALRM} = sub { die "Child server process timed out!\n" }; 493 | my $timeout = $self->{spampd}->{childtimeout}; 494 | 495 | # start a timeout alarm 496 | alarm($timeout); 497 | 498 | # start an smtp server 499 | my $smtp_server = SpamPD::Server->new($self->{server}->{client}); 500 | unless ( defined $smtp_server ) { 501 | die "WARNING!! Failed to create listening Server: $!"; } 502 | 503 | # start an smtp "client" (really a sending server) 504 | my $client = SpamPD::Client->new(interface => $self->{spampd}->{relayhost}, 505 | port => $self->{spampd}->{relayport}); 506 | unless ( defined $client ) { 507 | die "WARNING!! Failed to create sending Client: $!"; } 508 | 509 | # pass on initial client response 510 | $smtp_server->ok($client->hear) 511 | or die "WARNING!! Error in initial server->ok(client->hear): $!"; 512 | 513 | # while loop over incoming data from the server 514 | while ( my $what = $smtp_server->chat ) { 515 | 516 | # until end of DATA is sent, just pass the commands on transparently 517 | if ($what ne '.') { 518 | 519 | $client->say($what) 520 | or die "WARNING!! Failure in client->say(what): $!"; 521 | 522 | # but once the data is sent now we want to process it 523 | } else { 524 | 525 | # spam checking routine - message might be rewritten here 526 | $self->process_message($smtp_server->{data}) 527 | or die "WARNING!! Error processing message (process_message(data)): $!"; 528 | 529 | # $self->log(0, $smtp_server->{data}); #debug 530 | 531 | # need to give the client a rewound file 532 | $smtp_server->{data}->seek(0,0) 533 | or die "WARNING!! Can't rewind mail file: $!"; 534 | 535 | # now send the data on through the client 536 | $client->yammer($smtp_server->{data}) 537 | or die "WARNING!! Failure in client->yammer(smtp_server->{data}): $!"; 538 | 539 | #close the file 540 | $smtp_server->{data}->close 541 | or die "WARNING!! Couldn't close smtp_server->{data} temp file: $!"; 542 | 543 | } 544 | 545 | # pass on whatever the relayhost said in response 546 | $smtp_server->ok($client->hear) 547 | or die "WARNING!! Error in server->ok(client->hear): $!"; 548 | 549 | # restart the timeout alarm 550 | alarm($timeout); 551 | 552 | } # server ends connection 553 | 554 | # close connections 555 | $client->{sock}->close 556 | or die "WARNING!! Couldn't close client->{sock}: $!"; 557 | $smtp_server->{sock}->close 558 | or die "WARNING!! Couldn't close smtp_server->{sock}: $!"; 559 | 560 | }; # end eval block 561 | 562 | alarm(0); # stop the timer 563 | # check for error in eval block 564 | if ($@ ne '') { 565 | chomp($@); 566 | $msg = "WARNING!! Error in process_request eval block: $@"; 567 | $self->log(0, $msg); 568 | die ($msg . "\n"); 569 | } 570 | 571 | $self->{spampd}->{instance} = 1 unless defined $self->{spampd}->{instance}; 572 | exit 0 if $self->{spampd}->{instance}++ > $self->{spampd}->{maxrequests}; 573 | } 574 | 575 | my $relayhost = '127.0.0.1'; # relay to ip 576 | my $relayport = 25; # relay to port 577 | my $host = '127.0.0.1'; # listen on ip 578 | my $port = 10025; # listen on port 579 | my $maxrequests = 20; # max requests handled by child b4 dying 580 | my $childtimeout = 5*60; # child process per-command timeout in seconds 581 | my $pidfile = '/var/run/spampd.pid'; # write pid to file 582 | my $user = 'mail'; # user to run as 583 | my $group = 'mail'; # group to run as 584 | my $tagall = 0; # mark-up all msgs with SA, not just spam 585 | my $maxsize = 64; # max. msg size to scan with SA, in KB. 586 | 587 | # the following are deprecated as of v.2 588 | my $heloname = ''; 589 | my $dead_letters = ''; 590 | 591 | my %options = (port => \$port, 592 | host => \$host, 593 | relayhost => \$relayhost, 594 | relayport => \$relayport, 595 | 'dead-letters' => \$dead_letters, 596 | pid => \$pidfile, 597 | user => \$user, 598 | group => \$group, 599 | maxrequests => \$maxrequests, 600 | maxsize => \$maxsize, 601 | heloname => \$heloname, 602 | childtimeout => \$childtimeout 603 | ); 604 | 605 | usage(1) unless GetOptions(\%options, 606 | 'port=i', 607 | 'host=s', 608 | 'relayhost=s', 609 | 'relayport=i', 610 | 'maxrequests=i', 611 | 'dead-letters=s', 612 | 'user=s', 613 | 'group=s', 614 | 'pid=s', 615 | 'maxsize=i', 616 | 'heloname=s', 617 | 'tagall', 618 | 'auto-whitelist', 619 | 'stop-at-threshold', 620 | 'debug', 621 | 'help', 622 | 'local-only', 623 | 'childtimeout=i'); 624 | 625 | usage(0) if $options{help}; 626 | 627 | if ( $options{tagall} ) { $tagall = 1; } 628 | 629 | my @tmp = split (/:/, $relayhost); 630 | $relayhost = $tmp[0]; 631 | if ( $tmp[1] ) { $relayport = $tmp[1]; } 632 | 633 | @tmp = split (/:/, $host); 634 | $host = $tmp[0]; 635 | if ( $tmp[1] ) { $port = $tmp[1]; } 636 | 637 | 638 | my $assassin = Mail::SpamAssassin->new({ 639 | 'dont_copy_prefs' => 1, 640 | 'debug' => $options{'debug'} || 0, 641 | 'local_tests_only' => $options{'local-only'} || 0 }); 642 | 643 | # 'stop_at_threshold' => $options{'stop_at_threshold'} || 0, 644 | 645 | $options{'auto-whitelist'} and eval { 646 | require Mail::SpamAssassin::DBBasedAddrList; 647 | 648 | # create a factory for the persistent address list 649 | my $addrlistfactory = Mail::SpamAssassin::DBBasedAddrList->new(); 650 | $assassin->set_persistent_address_list_factory ($addrlistfactory); 651 | }; 652 | 653 | $assassin->compile_now(); 654 | 655 | my $server = bless { 656 | server => {host => $host, 657 | port => [ $port ], 658 | log_file => 'Sys::Syslog', 659 | syslog_ident => 'spampd', 660 | syslog_facility => 'mail', 661 | background => 1, 662 | pid_file => $pidfile, 663 | user => $user, 664 | group => $group, 665 | }, 666 | spampd => { maxrequests => $maxrequests, 667 | relayhost => $relayhost, 668 | relayport => $relayport, 669 | tagall => $tagall, 670 | maxsize => $maxsize, 671 | assassin => $assassin, 672 | childtimeout => $childtimeout 673 | }, 674 | }, 'SpamPD'; 675 | 676 | # call Net::Server to do the rest 677 | $server->run; 678 | 679 | exit 1; # shouldn't need this 680 | 681 | sub usage { 682 | print < 736 | [B<--host=host[:port]>] 737 | [B<--relayhost=hostname[:port]>] 738 | [B<--user=username>] 739 | [B<--group=groupname>] 740 | [B<--maxrequests=n>] 741 | [B<--childtimeout=n>] 742 | [B<--pid=filename>] 743 | [B<--maxsize=n>] 744 | [B<--tagall>] 745 | [B<--auto-whitelist>] 746 | [B<--local-only>] 747 | [B<--debug>] 748 | 749 | B B<--help> 750 | 751 | =head1 Description 752 | 753 | I is a relaying SMTP proxy that filters spam using 754 | SpamAssassin (http://www.SpamAssassin.org). The proxy is designed 755 | to be robust in the face of exceptional errors, and will (hopefully) 756 | never lose a message. 757 | 758 | I uses SpamAssassin to modify (tag) relayed messages based on 759 | their spam score, so all SA settings apply. This is described in the SA 760 | documentation. I will by default only tell SA to tag a 761 | message if it exceeds the spam threshold score, however you can have 762 | it rewrite all messages passing through by adding the --tagall option 763 | (see SA for how non-spam messages are tagged). 764 | 765 | I logs all aspects of its operation to syslog(8), using the 766 | mail syslog facility. 767 | 768 | The latest version can be found at 769 | http://www.WorldDesign.com/index.cfm/rd/mta/spampd.htm 770 | 771 | =head1 Requires 772 | 773 | =over 5 774 | 775 | Perl modules: 776 | 777 | =item B 778 | 779 | =item B 780 | 781 | =item B 782 | 783 | =item B 784 | 785 | =back 786 | 787 | =head1 Operation 788 | 789 | I is meant to operate as an SMTP mail proxy which passes 790 | each message through SpamAssassin for analysis. Note that I 791 | does not do anything other than check for spam, so it is not suitable as 792 | an anti-relay system. It is meant to work in conjunction with your 793 | regular mail system. Typically one would pipe any messages they wanted 794 | scanned through I after initial acceptance by your MX host. 795 | This is especially useful for using Postfix's (http://www.postfix.org) 796 | advanced content filtering mechanism, although certainly not limited to 797 | that application. 798 | 799 | Please re-read the second sentence in the above paragraph. You should NOT 800 | enable I to listen on a public interface (IP address) unless you 801 | know exactly what you're doing! It is very easy to set up an open relay this 802 | way. 803 | 804 | Note that I U I from the I distribution 805 | in function. You do not need to run I in order for I to function. 806 | 807 | Here are some simple examples (square brackets in the "diagrams" indicate 808 | physical machines): 809 | 810 | 811 | B 812 | 813 | =over 3 814 | 815 | The firewall/gateway MTA would be configured to forward all of its mail 816 | to the port that I listens on, and I would relay its 817 | messages to port 25 of your internal server. I could either 818 | run on its own host (and listen on any port) or it could run on either 819 | mail server (and listen on any port except port 25). 820 | 821 | Internet -> [ MX gateway (@inter.net.host:25) -> 822 | I (@localhost:2025) ] -> 823 | Internal mail (@private.host.ip:25) 824 | 825 | =back 826 | 827 | B 828 | 829 | =over 3 830 | 831 | Please see the FILTER_README that came with the Postfix distribution. You 832 | need to have a version of Postfix which supports this. 833 | 834 | Internet -> [ I (@inter.net.host:25) -> 835 | I (@localhost:10025) -> 836 | I (@localhost:10026) ] -> final delivery 837 | 838 | =back 839 | 840 | Note that these examples only show incoming mail delivery. Since it is 841 | usually unnecessary to scan mail coming from your network (right?), 842 | it may be desirable to set up a separate outbound route which bypasses 843 | I. 844 | 845 | 846 | =head1 Installation 847 | 848 | I can be run directly from the command prompt if desired. This is 849 | useful for testing purposes, but for long term use you probably want to put 850 | it somewhere like /usr/bin or /usr/local/bin and execute it at system startup. 851 | For example on Red Hat-style Linux system one can use a script in 852 | /etc/rc.d/init.d to start I (a sample script is available on the 853 | I Web page @ http://www.WorldDesign.com/index.cfm/rd/mta/spampd.htm). 854 | 855 | Note that I B I from the I distribution 856 | in function. You do not need to run I in order for I to function. 857 | This has apparently been the source of some confusion, so now you know. 858 | 859 | =head2 Postfix-specific Notes 860 | 861 | Here is a typical setup for Postfix "advanced" content filtering as described 862 | in the FILTER_README that came with the Postfix distribution: 863 | 864 | F: 865 | 866 | smtp inet n - y - - smtpd 867 | -o content_filter=smtp:localhost:10025 868 | -o myhostname=mx.example.com 869 | 870 | localhost:10026 inet n - n - 10 smtpd 871 | -o content_filter= 872 | -o myhostname=mx-int.example.com 873 | 874 | The first entry is the main public-facing MTA which uses localhost:10025 875 | as the content filter for all mail. The second entry receives mail from 876 | the content filter and does final delivery. Both smtpd instances use 877 | the same Postfix F file. I is the process that listens on 878 | localhost:10025 and then connects to the Postfix listener on localhost:10026. 879 | Note that the C options must be different between the two instances, 880 | otherwise Postfix will think it's talking to itself and abort sending. 881 | 882 | For the above example you can simply start I like this: 883 | 884 | spampd --host=localhost:10025 --relayhost=localhost:10026 885 | 886 | =head1 Options 887 | 888 | =over 5 889 | 890 | =item B<--host=ip or hostname[:port]> 891 | 892 | Specifies what hostname/IP and port I listens on. By default, it listens 893 | on 127.0.0.1 (localhost) on port 10025. 894 | 895 | B You should NOT enable I to listen on a 896 | public interface (IP address) unless you know exactly what you're doing! 897 | 898 | =item B<--port=n> 899 | 900 | Specifies what port I listens on. By default, it listens on 901 | port 10025. This is an alternate to using the above --host=ip:port notation. 902 | 903 | =item B<--relayhost=ip or hostname[:port]> 904 | 905 | Specifies the hostname where I will relay all 906 | messages. Defaults to 127.0.0.1. If the port is not provided, that 907 | defaults to 25. 908 | 909 | =item B<--relayport=n> 910 | 911 | Specifies what port I will relay to. Default is 35. This is an 912 | alternate to using the above --relayhost=ip:port notation. 913 | 914 | =item B<--user=username> 915 | 916 | =item B<--group=groupname> 917 | 918 | Specifies the user and group that the proxy will run as. Default is 919 | I/I. 920 | 921 | =item B<--maxrequests=n> 922 | 923 | I works by forking child servers to handle each message. The 924 | B parameter specifies how many requests will be handled 925 | before the child exits. Since a child never gives back memory, a large 926 | message can cause it to become quite bloated; the only way to reclaim 927 | the memory is for the child to exit. The default is 20. 928 | 929 | =item B<--childtimeout=n> 930 | 931 | This is the number of seconds to allow each child server before it times out 932 | a transaction. In an SMTP transaction the timer is reset for every command. This 933 | timeout includes time it would take to send the message data, so it should not 934 | be too short. Default is 300 seconds (5 minutes). 935 | 936 | =item B<--pid=filename> 937 | 938 | Specifies a filename where I will write its process ID so 939 | that it is easy to kill it later. The directory that will contain this 940 | file must be writable by the I user. The default is 941 | F. 942 | 943 | =item B<--tagall> 944 | 945 | Tells I to have SpamAssassin add headers to all scanned mail, 946 | not just spam. By default I will only rewrite messages which 947 | exceed the spam threshold score (as defined in the SA settings). 948 | 949 | =item B<--maxsize=n> 950 | 951 | The maximum message size to send to SpamAssassin, in KB. By default messages 952 | over 64KB are not scanned at all, and an appropriate message is logged 953 | indicating this. This includes headers. 954 | 955 | =item B<--auto-whitelist> 956 | 957 | Turns on the SpamAssassin global whitelist feature. See the SA docs. Note 958 | that per-user whitelists are not available. 959 | 960 | =item B<--local-only> 961 | 962 | Turn off all SA network-based tests (DNS, Razor, etc). 963 | 964 | =item B<--debug> 965 | 966 | Turns on SpamAssassin debug messages. 967 | 968 | =item B<--help> 969 | 970 | Prints usage information. 971 | 972 | =back 973 | 974 | =head2 Deprecated Options 975 | 976 | =over 5 977 | 978 | The following options are no longer used but still accepted for backwards 979 | compatibility with I v1: 980 | 981 | =item B<--dead-letters> 982 | 983 | =item B<--heloname> 984 | 985 | =item B<--stop-at-threshold> 986 | 987 | =back 988 | 989 | =head1 Examples 990 | 991 | =over 5 992 | 993 | =item Running between firewall/gateway and internal mail server 994 | 995 | 996 | I listens on port 10025 on the same host as the internal mail server. 997 | 998 | spampd --host=192.168.1.10 999 | 1000 | Same as above but I runs on port 10025 of the same host as 1001 | the firewall/gateway and passes messages on to the internal mail server 1002 | on another host. 1003 | 1004 | spampd --relayhost=192.168.1.10 1005 | 1006 | =item Using Postfix advanced content filtering example 1007 | and the SA auto-whitelist feature 1008 | 1009 | spampd --port=10025 --relayhost=127.0.0.1:10026 --auto-whitelist 1010 | 1011 | =back 1012 | 1013 | =head1 Credits 1014 | 1015 | I is written and maintained by Maxim Paperno . 1016 | See http://www.WorldDesign.com/index.cfm/rd/mta/spampd.htm for latest info. 1017 | 1018 | I v2 uses two Perl modules by Bennett Todd and Copyright (C) 2001 Morgan 1019 | Stanley Dean Witter. These are distributed under the GNU GPL (see 1020 | module code for more details). Both modules have been slightly modified 1021 | from the originals and are included in this file under new names. 1022 | 1023 | Also thanks to Bennet Todd for the example smtpproxy script which helped create 1024 | this version of I. See http://bent.latency.net/smtpprox/ . 1025 | 1026 | I v1 was based on code by Dave Carrigan named assassind. Trace amounts 1027 | of his code or documentation may still remain. Thanks to him for the 1028 | original inspiration and code. See http://www.rudedog.org/assassind/ . 1029 | 1030 | Also thanks to I (included with SpamAssassin) and 1031 | I (http://www.ijs.si/software/amavisd/) for some tricks. 1032 | 1033 | =head1 Copyright and Disclaimer 1034 | 1035 | I is Copyright (c) 2002 by World Design Group and Maxim Paperno 1036 | 1037 | Portions are Copyright (C) 2001 Morgan Stanley Dean Witter as mentioned above 1038 | in the CREDITS section. 1039 | 1040 | This program is free software; you can redistribute it and/or modify 1041 | it under the terms of the GNU General Public License as published by 1042 | the Free Software Foundation; either version 2 of the License, or 1043 | (at your option) any later version. 1044 | 1045 | This program is distributed in the hope that it will be useful, 1046 | but WITHOUT ANY WARRANTY; without even the implied warranty of 1047 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1048 | GNU General Public License for more details. 1049 | 1050 | The GNU GPL can be found at http://www.fsf.org/copyleft/gpl.html 1051 | 1052 | 1053 | =head1 Bugs 1054 | 1055 | None known. Please report any to MPaperno@WorldDesign.com. 1056 | 1057 | =head1 To Do 1058 | 1059 | Add configurable option for rejecting mail outright based on spam score. 1060 | It would be nice to make this program safe enough to sit in front of a mail 1061 | server such as Postfix and be able to reject mail before it enters our systems. 1062 | The only real problem is that Postfix will see localhost as the connecting 1063 | client, so that disables any client-based checks Postfix can do and creates a 1064 | possible relay hole if localhost is trusted. 1065 | 1066 | Make it handle LMTP protocol. 1067 | 1068 | =head1 See Also 1069 | 1070 | perl(1), Spam::Assassin(3), L, 1071 | L 1072 | -------------------------------------------------------------------------------- /previous-versions/spampd-2.10.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl -T 2 | 3 | ###################### 4 | # SpamPD - spam proxy daemon 5 | # 6 | # v2.10 - 01-Jul-03 7 | # v2.00 - 10-Jun-03 8 | # v1.0.2 - 13-Apr-03 9 | # v1.0.1 - 03-Feb-03 10 | # v1.0.0 - May 2002 11 | # 12 | # spampd is Copyright (c) 2002 by World Design Group and Maxim Paperno 13 | # (see http://www.WorldDesign.com/index.cfm/rd/mta/spampd.htm) 14 | # 15 | # Written and maintained by Maxim Paperno (MPaperno@WorldDesign.com) 16 | # 17 | # This program is free software; you can redistribute it and/or modify 18 | # it under the terms of the GNU General Public License as published by 19 | # the Free Software Foundation; either version 2 of the License, or 20 | # (at your option) any later version. 21 | # 22 | # This program is distributed in the hope that it will be useful, 23 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 | # GNU General Public License for more details. 26 | # 27 | # The GNU GPL can be found at http://www.fsf.org/copyleft/gpl.html 28 | # 29 | # spampd v2 uses two Perl modules by Bennett Todd and Copyright (C) 2001 Morgan 30 | # Stanley Dean Witter. These are also distributed under the GNU GPL (see 31 | # module code for more details). Both modules have been slightly modified 32 | # from the originals and are included in this file under new names. 33 | # 34 | # spampd v1 was based on code by Dave Carrigan named assassind. Trace amounts 35 | # of his code or documentation may still remain. Thanks to him for the 36 | # original inspiration and code. (see http://www.rudedog.org/assassind/) 37 | # 38 | ###################### 39 | 40 | 41 | ################################################################################ 42 | package SpamPD::Server; 43 | 44 | # Originally known as MSDW::SMTP::Server 45 | # 46 | # This code is Copyright (C) 2001 Morgan Stanley Dean Witter, and 47 | # is distributed according to the terms of the GNU Public License 48 | # as found at . 49 | # 50 | # Modified for use in SpamPD by Maxim Paperno (June, 2003) 51 | # 52 | # This program is distributed in the hope that it will be useful, 53 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 54 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 55 | # GNU General Public License for more details. 56 | # 57 | # Written by Bennett Todd 58 | 59 | # =item DESCRIPTION 60 | # 61 | # This server simply gathers the SMTP acquired information (envelope 62 | # sender and recipient, and data) into unparsed memory buffers (or a 63 | # file for the data), and returns control to the caller to explicitly 64 | # acknowlege each command or request. Since acknowlegement or failure 65 | # are driven explicitly from the caller, this module can be used to 66 | # create a robust SMTP content scanning proxy, transparent or not as 67 | # desired. 68 | # 69 | # =cut 70 | 71 | use strict; 72 | use IO::File; 73 | #use IO::Socket; 74 | 75 | # =item new(interface => $interface, port => $port); 76 | 77 | # The #interface and port to listen on must be specified. The interface 78 | # must be a valid numeric IP address (0.0.0.0 to listen on all 79 | # interfaces, as usual); the port must be numeric. If this call 80 | # succeeds, it returns a server structure with an open 81 | # IO::Socket::INET in it, ready to listen on. If it fails it dies, so 82 | # if you want anything other than an exit with an explanatory error 83 | # message, wrap the constructor call in an eval block and pull the 84 | # error out of $@ as usual. This is also the case for all other 85 | # methods; they succeed or they die. 86 | # 87 | # =cut 88 | 89 | sub new { 90 | 91 | # This now emulates Net::SMTP::Server::Client for use with Net::Server which 92 | # passes an already open socket. 93 | 94 | my($this, $socket) = @_; 95 | 96 | my $class = ref($this) || $this; 97 | my $self = {}; 98 | $self->{sock} = $socket; 99 | 100 | bless($self, $class); 101 | 102 | die "$0: socket bind failure: $!\n" unless defined $self->{sock}; 103 | $self->{state} = 'started'; 104 | return $self; 105 | 106 | # Original code, removed by MP for spampd use 107 | # 108 | # my ($this, @opts) = @_; 109 | # my $class = ref($this) || $this; 110 | # my $self = bless { @opts }, $class; 111 | # $self->{sock} = IO::Socket::INET->new( 112 | # LocalAddr => $self->{interface}, 113 | # LocalPort => $self->{port}, 114 | # Proto => 'tcp', 115 | # Type => SOCK_STREAM, 116 | # Listen => 65536, 117 | # Reuse => 1, 118 | # ); 119 | # die "$0: socket bind failure: $!\n" unless defined $self->{sock}; 120 | # $self->{state} = 'just bound', 121 | # return $self; 122 | 123 | } 124 | 125 | # sub accept { } 126 | # 127 | # Removed by MP; not needed for spampd use 128 | # 129 | 130 | 131 | # =item chat; 132 | # 133 | # The chat method carries the SMTP dialogue up to the point where any 134 | # acknowlegement must be made. If chat returns true, then its return 135 | # value is the previous SMTP command. If the return value begins with 136 | # 'mail' (case insensitive), then the attribute 'from' has been filled 137 | # in, and may be checked; if the return value begins with 'rcpt' then 138 | # both from and to have been been filled in with scalars, and should 139 | # be checked, then either 'ok' or 'fail' should be called to accept 140 | # or reject the given sender/recipient pair. If the return value is 141 | # 'data', then the attributes from and to are populated; in this case, 142 | # the 'to' attribute is a reference to an anonymous array containing 143 | # all the recipients for this data. If the return value is '.', then 144 | # the 'data' attribute (which may be pre-populated in the "new" or 145 | # "accept" methods if desired) is a reference to a filehandle; if it's 146 | # created automatically by this module it will point to an unlinked 147 | # tmp file in /tmp. If chat returns false, the SMTP dialogue has been 148 | # completed and the socket closed; this server is ready to exit or to 149 | # accept again, as appropriate for the server style. 150 | # 151 | # The return value from chat is also remembered inside the server 152 | # structure in the "state" attribute. 153 | # 154 | # =cut 155 | 156 | sub chat { 157 | my ($self) = @_; 158 | local(*_); 159 | if ($self->{state} !~ /^data/i) { 160 | return 0 unless defined($_ = $self->_getline); 161 | s/[\r\n]*$//; 162 | $self->{state} = $_; 163 | if (s/^.?he?lo\s+//i) { # mp: find helo|ehlo|lhlo 164 | # mp: determine protocol (for future use) 165 | if ( /^L/i ) { 166 | $self->{proto} = "lmtp"; 167 | } elsif ( /^E/i ) { 168 | $self->{proto} = "esmtp"; 169 | } else { 170 | $self->{proto} = "smtp"; } 171 | s/\s*$//; 172 | s/\s+/ /g; 173 | $self->{helo} = $_; 174 | } elsif (s/^rset\s*//i) { 175 | delete $self->{to}; 176 | delete $self->{data}; 177 | delete $self->{recipients}; 178 | } elsif (s/^mail\s+from:\s*//i) { 179 | delete $self->{to}; 180 | delete $self->{data}; 181 | delete $self->{recipients}; 182 | s/\s*$//; 183 | $self->{from} = $_; 184 | } elsif (s/^rcpt\s+to:\s*//i) { 185 | s/\s*$//; s/\s+/ /g; 186 | $self->{to} = $_; 187 | push @{$self->{recipients}}, $_; 188 | } elsif (/^data/i) { 189 | $self->{to} = $self->{recipients}; 190 | } 191 | } else { 192 | if (defined($self->{data})) { 193 | $self->{data}->seek(0, 0); 194 | $self->{data}->truncate(0); 195 | } else { 196 | $self->{data} = IO::File->new_tmpfile; 197 | } 198 | while (defined($_ = $self->_getline)) { 199 | if ($_ eq ".\r\n") { 200 | $self->{data}->seek(0,0); 201 | return $self->{state} = '.'; 202 | } 203 | s/^\.\./\./; 204 | $self->{data}->print($_) or die "$0: write error saving data\n"; 205 | } 206 | return(0); 207 | } 208 | return $self->{state}; 209 | } 210 | 211 | # =item ok([message]); 212 | # 213 | # Approves of the data given to date, either the recipient or the 214 | # data, in the context of the sender [and, for data, recipients] 215 | # already given and available as attributes. If a message is given, it 216 | # will be sent instead of the internal default. 217 | # 218 | # =cut 219 | 220 | sub ok { 221 | my ($self, @msg) = @_; 222 | @msg = ("250 ok.") unless @msg; 223 | $self->_print("@msg\r\n") or 224 | die "$0: write error acknowledging $self->{state}: $!\n"; 225 | } 226 | 227 | # =item fail([message]); 228 | # 229 | # Rejects the current info; if processing from, rejects the sender; if 230 | # processing 'to', rejects the current recipient; if processing data, 231 | # rejects the entire message. If a message is specified it means the 232 | # exact same thing as "ok" --- simply send that message to the sender. 233 | # 234 | # =cut 235 | 236 | sub fail { 237 | my ($self, @msg) = @_; 238 | @msg = ("550 no.") unless @msg; 239 | $self->_print("@msg\r\n") or 240 | die "$0: write error acknowledging $self->{state}: $!\n"; 241 | } 242 | 243 | # utility functions 244 | 245 | sub _getline { 246 | my ($self) = @_; 247 | local ($/) = "\r\n"; 248 | my $tmp = $self->{sock}->getline; 249 | if ( defined $self->{debug} ) { 250 | $self->{debug}->print($tmp) if ($tmp); 251 | } 252 | return $tmp; 253 | } 254 | 255 | sub _print { 256 | my ($self, @msg) = @_; 257 | $self->{debug}->print(@msg) if defined $self->{debug}; 258 | $self->{sock}->print(@msg); 259 | } 260 | 261 | 1; 262 | 263 | ################################################################################ 264 | package SpamPD::Client; 265 | 266 | # Originally known as MSDW::SMTP::Client 267 | # 268 | # This code is Copyright (C) 2001 Morgan Stanley Dean Witter, and 269 | # is distributed according to the terms of the GNU Public License 270 | # as found at . 271 | # 272 | # Modified for use in SpamPD by Maxim Paperno (June, 2003) 273 | # 274 | # This program is distributed in the hope that it will be useful, 275 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 276 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 277 | # GNU General Public License for more details. 278 | # 279 | # Written by Bennett Todd 280 | 281 | # =head1 DESCRIPTION 282 | # 283 | # MSDW::SMTP::Client provides a very lean SMTP client implementation; 284 | # the only protocol-specific knowlege it has is the structure of SMTP 285 | # multiline responses. All specifics lie in the hands of the calling 286 | # program; this makes it appropriate for a semi-transparent SMTP 287 | # proxy, passing commands between a talker and a listener. 288 | # 289 | # =cut 290 | 291 | use strict; 292 | use IO::Socket; 293 | 294 | # =item new(interface => $interface, port => $port[, timeout = 300]); 295 | # 296 | # The interface and port to talk to must be specified. The interface 297 | # must be a valid numeric IP address; the port must be numeric. If 298 | # this call succeeds, it returns a client structure with an open 299 | # IO::Socket::INET in it, ready to talk to. If it fails it dies, 300 | # so if you want anything other than an exit with an explanatory 301 | # error message, wrap the constructor call in an eval block and pull 302 | # the error out of $@ as usual. This is also the case for all other 303 | # methods; they succeed or they die. The timeout parameter is passed 304 | # on into the IO::Socket::INET constructor. 305 | # 306 | # =cut 307 | 308 | sub new { 309 | my ($this, @opts) = @_; 310 | my $class = ref($this) || $this; 311 | my $self = bless { timeout => 300, @opts }, $class; 312 | $self->{sock} = IO::Socket::INET->new( 313 | PeerAddr => $self->{interface}, 314 | PeerPort => $self->{port}, 315 | Timeout => $self->{timeout}, 316 | Proto => 'tcp', 317 | Type => SOCK_STREAM, 318 | ); 319 | die "$0: socket connect failure: $!\n" unless defined $self->{sock}; 320 | return $self; 321 | } 322 | 323 | # =item hear 324 | # 325 | # hear collects a complete SMTP response and returns it with trailing 326 | # CRLF removed; for multi-line responses, intermediate CRLFs are left 327 | # intact. Returns undef if EOF is seen before a complete reply is 328 | # collected. 329 | # 330 | # =cut 331 | 332 | sub hear { 333 | my ($self) = @_; 334 | my ($tmp, $reply); 335 | return undef unless $tmp = $self->{sock}->getline; 336 | while ($tmp =~ /^\d{3}-/) { 337 | $reply .= $tmp; 338 | return undef unless $tmp = $self->{sock}->getline; 339 | } 340 | $reply .= $tmp; 341 | $reply =~ s/\r\n$//; 342 | return $reply; 343 | } 344 | 345 | # =item say("command text") 346 | # 347 | # say sends an SMTP command, appending CRLF. 348 | # 349 | # =cut 350 | 351 | sub say { 352 | my ($self, @msg) = @_; 353 | return unless @msg; 354 | $self->{sock}->print("@msg", "\r\n") or die "$0: write error: $!"; 355 | } 356 | 357 | # =item yammer(FILEHANDLE) 358 | # 359 | # yammer takes a filehandle (which should be positioned at the 360 | # beginning of the file, remember to $fh->seek(0,0) if you've just 361 | # written it) and sends its contents as the contents of DATA. This 362 | # should only be invoked after a $client->say("data") and a 363 | # $client->hear to collect the reply to the data command. It will send 364 | # the trailing "." as well. It will perform leading-dot-doubling in 365 | # accordance with the SMTP protocol spec, where "leading dot" is 366 | # defined in terms of CR-LF terminated lines --- i.e. the data should 367 | # contain CR-LF data without the leading-dot-quoting. The filehandle 368 | # will be left at EOF. 369 | # 370 | # =cut 371 | 372 | sub yammer { 373 | my ($self, $fh) = (@_); 374 | local (*_); 375 | local ($/) = "\r\n"; 376 | while (<$fh>) { 377 | s/^\./../; 378 | $self->{sock}->print($_) or die "$0: write error: $!\n"; 379 | } 380 | $self->{sock}->print(".\r\n") or die "$0: write error: $!\n"; 381 | } 382 | 383 | 1; 384 | 385 | 386 | ################################################################################ 387 | package SpamPD; 388 | 389 | use strict; 390 | use Net::Server::PreForkSimple; 391 | use IO::File; 392 | use Getopt::Long; 393 | use Mail::SpamAssassin; 394 | use Mail::SpamAssassin::NoMailAudit; 395 | 396 | BEGIN { 397 | # Load Time::HiRes if it's available 398 | eval { require Time::HiRes }; 399 | Time::HiRes->import( qw(time) ) unless $@; 400 | 401 | # use included modules 402 | import SpamPD::Server; 403 | import SpamPD::Client; 404 | } 405 | 406 | 407 | use vars qw(@ISA $VERSION); 408 | our @ISA = qw(Net::Server::PreForkSimple); 409 | our $VERSION = '2.00'; 410 | 411 | sub process_message { 412 | my ($self, $fh) = @_; 413 | 414 | # output lists with a , delimeter by default 415 | local ($") = ","; 416 | 417 | # start a timer 418 | my $start = time; 419 | # use the assassin object created during startup 420 | my $assassin = $self->{spampd}->{assassin}; 421 | 422 | # this gets info about the message temp file 423 | (my $dev,my $ino,my $mode,my $nlink,my $uid, 424 | my $gid,my $rdev,my $size, 425 | my $atime,my $mtime,my $ctime, 426 | my $blksize,my $blocks) = $fh->stat or die "Can't stat mail file: $!"; 427 | 428 | # Only process message under --maxsize KB 429 | if ( $size < ($self->{spampd}->{maxsize} * 1024) ) { 430 | 431 | # read message into array of lines to feed to SA 432 | # notes in the SA::NoMailAudit code indicate it should take a 433 | # filehandle... but that doesn't seem to work 434 | my (@msglines, $msgid, $tmp); 435 | $fh->seek(0,0) or die "Can't rewind message file: $!"; 436 | 437 | # loop over headers first (we may want info from them) 438 | while (<$fh>) { 439 | 440 | # if last line 441 | # if (/^\r?\n$/) { 442 | # if ( $self->{spampd}->{addheader} && length($self->{spampd}->{myhostname}) ) { 443 | # $tmp = "X-Spam-Scanned-By: $self->{spampd}->{myhostname}\n"; 444 | # push(@msglines, $tmp); 445 | # } 446 | # } 447 | push(@msglines, $_); 448 | 449 | # find the Message-ID for logging (code is from spamd) 450 | if (/^Message-Id:\s+(.*?)\s*$/i) { 451 | $msgid = $1; 452 | while($msgid =~ s/\([^\(\)]*\)//) {}; # remove comments and 453 | $msgid =~ s/^\s+|\s+$//g; # leading and trailing spaces 454 | $msgid =~ s/\s.*$//; # keep only the first token 455 | } 456 | 457 | last if (/^\r?\n$/); 458 | } 459 | 460 | # finish loop over rest of body 461 | while (<$fh>) { push(@msglines, $_); } 462 | 463 | # my @resplines = @msglines; 464 | 465 | my $recips = "@{$self->{smtp_server}->{to}}"; 466 | $msgid ||= "(unknown)"; 467 | $recips ||= "(unknown)"; 468 | 469 | $self->log(2, "processing message $msgid for ". $recips); 470 | 471 | eval { 472 | 473 | local $SIG{ALRM} = sub { die "Timed out!\n" }; 474 | # save previous timer and start new 475 | my $previous_alarm = alarm($self->{spampd}->{satimeout}); 476 | 477 | # Audit the message 478 | my $mail = Mail::SpamAssassin::NoMailAudit->new ( 479 | data => \@msglines ); 480 | 481 | # Check spamminess 482 | my $status = $assassin->check($mail); 483 | 484 | if ( $self->{spampd}->{debug} ) { 485 | $self->log(2, "Returned from checking by SpamAssassin"); } 486 | 487 | if ( $self->{spampd}->{addheader} && length($self->{spampd}->{myhostname}) ) { 488 | $mail->put_header("X-Spam-Checked-By", $self->{spampd}->{myhostname}); 489 | } 490 | 491 | # Rewrite mail if high spam factor or options --tagall or --add-sc-header 492 | if ( $status->is_spam || $self->{spampd}->{tagall} || $self->{spampd}->{addheader} ) { 493 | 494 | # if spam or --tagall, rewrite using SA 495 | if ( $status->is_spam || $self->{spampd}->{tagall} ) { 496 | if ( $self->{spampd}->{debug} ) { 497 | $self->log(2, "Rewriting mail using SpamAssassin"); } 498 | 499 | $status->rewrite_mail; 500 | 501 | } 502 | 503 | my $msg_resp = join '',$mail->header,"\r\n",@{$mail->body}; 504 | my @resplines = split(/\r?\n/, $msg_resp); 505 | 506 | # Build the new message to relay 507 | # pause the timeout alarm while we do this (no point in timing 508 | # out here and leaving a half-written file). 509 | my $pause_alarm = alarm(0); 510 | $fh->seek(0,0) or die "Can't rewind message file: $!"; 511 | $fh->truncate(0) or die "Can't truncate message file: $!"; 512 | for (@resplines) { 513 | $fh->print($_ . "\r\n") 514 | or die "Can't print to message file: $!"; 515 | } 516 | #restart the alarm 517 | alarm($pause_alarm); 518 | 519 | } 520 | 521 | # Log what we did 522 | my $was_it_spam = 'clean message'; 523 | if ($status->is_spam) { $was_it_spam = 'identified spam'; } 524 | my $msg_score = sprintf("%.2f",$status->get_hits); 525 | my $msg_threshold = sprintf("%.2f",$status->get_required_hits); 526 | my $proc_time = sprintf("%.2f", time - $start); 527 | 528 | $self->log(2, "$was_it_spam $msgid ($msg_score/$msg_threshold) for ". 529 | "$recips in $proc_time seconds, $size bytes."); 530 | 531 | # thanks to Kurt Andersen for this idea 532 | if ( $self->{spampd}->{rh} ) { 533 | $self->log(2, "rules hit for $msgid: " . $status->get_names_of_tests_hit); } 534 | 535 | $status->finish(); 536 | 537 | # set the timeout alarm back to wherever it was at 538 | alarm($previous_alarm); 539 | 540 | }; 541 | 542 | if ( $@ ne '' ) { 543 | $self->log(1, "WARNING!! SpamAssassin error on message $msgid: $@"); 544 | return 0; 545 | } 546 | 547 | } else { 548 | 549 | $self->log(2, "skipped large message (". $size / 1024 ."KB)"); 550 | 551 | } 552 | 553 | return 1; 554 | 555 | } 556 | 557 | sub process_request { 558 | my $self = shift; 559 | my $msg; 560 | 561 | eval { 562 | 563 | local $SIG{ALRM} = sub { die "Child server process timed out!\n" }; 564 | my $timeout = $self->{spampd}->{childtimeout}; 565 | 566 | # start a timeout alarm 567 | alarm($timeout); 568 | 569 | # start an smtp server 570 | my $smtp_server = SpamPD::Server->new($self->{server}->{client}); 571 | unless ( defined $smtp_server ) { 572 | die "Failed to create listening Server: $!"; } 573 | 574 | $self->{smtp_server} = $smtp_server; 575 | 576 | if ( $self->{spampd}->{debug} ) { 577 | $self->log(2, "Initiated Server"); } 578 | 579 | # start an smtp "client" (really a sending server) 580 | my $client = SpamPD::Client->new(interface => $self->{spampd}->{relayhost}, 581 | port => $self->{spampd}->{relayport}); 582 | unless ( defined $client ) { 583 | die "Failed to create sending Client: $!"; } 584 | 585 | if ( $self->{spampd}->{debug} ) { 586 | $self->log(2, "Initiated Client"); } 587 | 588 | # pass on initial client response 589 | # $client->hear can handle multiline responses so no need to loop 590 | $smtp_server->ok($client->hear) 591 | or die "Error in initial server->ok(client->hear): $!"; 592 | 593 | if ( $self->{spampd}->{debug} ) { 594 | $self->log(2, "smtp_server state: '" . $smtp_server->{state} . "'"); } 595 | 596 | # while loop over incoming data from the server 597 | while ( my $what = $smtp_server->chat ) { 598 | 599 | if ( $self->{spampd}->{debug} ) { 600 | $self->log(2, "smtp_server state: '" . $smtp_server->{state} . "'"); } 601 | 602 | # until end of DATA is sent, just pass the commands on transparently 603 | if ($what ne '.') { 604 | 605 | $client->say($what) 606 | or die "Failure in client->say(what): $!"; 607 | 608 | # but once the data is sent now we want to process it 609 | } else { 610 | 611 | # spam checking routine - message might be rewritten here 612 | my $pmrescode = $self->process_message($smtp_server->{data}); 613 | 614 | # pass on the messsage if exit code <> 0 or die-on-sa-errors flag is off 615 | if ( $pmrescode or !$self->{spampd}->{dose} ) { 616 | 617 | # need to give the client a rewound file 618 | $smtp_server->{data}->seek(0,0) 619 | or die "Can't rewind mail file: $!"; 620 | 621 | # now send the data on through the client 622 | $client->yammer($smtp_server->{data}) 623 | or die "Failure in client->yammer(smtp_server->{data}): $!"; 624 | 625 | } else { 626 | 627 | $smtp_server->ok("450 Temporary failure processing message, please try again later"); 628 | last; 629 | } 630 | 631 | #close the temp file 632 | $smtp_server->{data}->close 633 | or $self->log(1, "WARNING!! Couldn't close smtp_server->{data} temp file: $!"); 634 | 635 | if ( $self->{spampd}->{debug} ) { 636 | $self->log(2, "Finished sending DATA"); } 637 | } 638 | 639 | # pass on whatever the relayhost said in response 640 | # $client->hear can handle multiline responses so no need to loop 641 | my $destresp = $client->hear; 642 | $smtp_server->ok($destresp) 643 | or die "Error in server->ok(client->hear): $!"; 644 | 645 | if ( $self->{spampd}->{debug} ) { 646 | $self->log(2, "Destination response: '" . $destresp . "'"); } 647 | 648 | # restart the timeout alarm 649 | alarm($timeout); 650 | 651 | } # server ends connection 652 | 653 | # close connections 654 | $client->{sock}->close 655 | or die "Couldn't close client->{sock}: $!"; 656 | $smtp_server->{sock}->close 657 | or die "Couldn't close smtp_server->{sock}: $!"; 658 | 659 | if ( $self->{spampd}->{debug} ) { 660 | $self->log(2, "Closed connections"); } 661 | 662 | }; # end eval block 663 | 664 | alarm(0); # stop the timer 665 | # check for error in eval block 666 | if ($@ ne '') { 667 | chomp($@); 668 | $msg = "WARNING!! Error in process_request eval block: $@"; 669 | $self->log(0, $msg); 670 | die ($msg . "\n"); 671 | } 672 | 673 | $self->{spampd}->{instance}++; 674 | 675 | # if ( $self->{spampd}->{instance}++ > $self->{spampd}->{maxrequests} ) { 676 | 677 | # if ( $self->{spampd}->{debug} ) { 678 | # $self->log(2, "Exiting child process after handling ". 679 | # $self->{spampd}->{maxrequests} ." requests"); } 680 | 681 | # exit 0; 682 | 683 | # }; 684 | 685 | } 686 | 687 | # Net::Server hook 688 | # about to exit child process 689 | sub child_finish_hook { 690 | my($self) = shift; 691 | if ( $self->{spampd}->{debug} ) { 692 | $self->log(2, "Exiting child process after handling ". 693 | $self->{spampd}->{instance} ." requests"); } 694 | } 695 | 696 | 697 | my $relayhost = '127.0.0.1'; # relay to ip 698 | my $relayport = 25; # relay to port 699 | my $host = '127.0.0.1'; # listen on ip 700 | my $port = 10025; # listen on port 701 | my $children = 5; # number of child processes (servers) to spawn at start 702 | # my $maxchildren = $children; # max. number of child processes (servers) to spawn 703 | my $maxrequests = 20; # max requests handled by child b4 dying 704 | my $childtimeout = 6*60; # child process per-command timeout in seconds 705 | my $satimeout = 285; # SpamAssassin timeout in seconds (15s less than Postfix 706 | # default for smtp_data_done_timeout) 707 | my $pidfile = '/var/run/spampd.pid'; # write pid to file 708 | my $user = 'mail'; # user to run as 709 | my $group = 'mail'; # group to run as 710 | my $tagall = 0; # mark-up all msgs with SA, not just spam 711 | my $maxsize = 64; # max. msg size to scan with SA, in KB. 712 | my $rh = 0; # log which rules were hit 713 | my $debug = 0; # debug flag 714 | my $dose = 0; # die-on-sa-errors flag 715 | my $addheader = 0; # add X-Spam-Checked-By header to all messages 716 | # hostname to use in X-Spam-Checked-By header: 717 | my $myhostname = ( length($ENV{HOSTNAME}) ) ? $ENV{HOSTNAME} : "localhost"; 718 | 719 | # the following are deprecated as of v.2 720 | my $heloname = ''; 721 | my $dead_letters = ''; 722 | 723 | my %options = (port => \$port, 724 | host => \$host, 725 | relayhost => \$relayhost, 726 | relayport => \$relayport, 727 | 'dead-letters' => \$dead_letters, 728 | pid => \$pidfile, 729 | user => \$user, 730 | group => \$group, 731 | maxrequests => \$maxrequests, 732 | maxsize => \$maxsize, 733 | heloname => \$heloname, 734 | childtimeout => \$childtimeout, 735 | satimeout => \$satimeout, 736 | children => \$children, 737 | # maxchildren => \$maxchildren, 738 | hostname => \$myhostname 739 | ); 740 | 741 | usage(1) unless GetOptions(\%options, 742 | 'port=i', 743 | 'host=s', 744 | 'relayhost=s', 745 | 'relayport=i', 746 | 'children|c=i', 747 | # 'maxchildren|mc=i', 748 | 'maxrequests|mr=i', 749 | 'childtimeout=i', 750 | 'satimeout=i', 751 | 'dead-letters=s', 752 | 'user|u=s', 753 | 'group|g=s', 754 | 'pid|p=s', 755 | 'maxsize=i', 756 | 'heloname=s', 757 | 'tagall|a', 758 | 'auto-whitelist|aw', 759 | 'stop-at-threshold', 760 | 'debug|d', 761 | 'help|h', 762 | 'local-only|l', 763 | 'log-rules-hit|rh', 764 | 'dose', 765 | 'add-sc-header|ash', 766 | 'hostname=s' 767 | ); 768 | 769 | usage(0) if $options{help}; 770 | 771 | if ( $options{tagall} ) { $tagall = 1; } 772 | if ( $options{'log-rules-hit'} ) { $rh = 1; } 773 | if ( $options{debug} ) { $debug = 1; } 774 | if ( $options{dose} ) { $dose = 1; } 775 | if ( $options{'add-sc-header'} ) { $addheader = 1; } 776 | # if ( !$options{maxchildren} or $maxchildren < $children ) { $maxchildren = $children; } 777 | 778 | if ( $children < 1 ) { print "Option --children must be greater than zero!\n"; exit shift; } 779 | 780 | # my $min_spare_servers = ($children == $maxchildren) ? 0 : 1; 781 | # my $max_spare_servers = ($min_spare_servers == 0) ? 0 : $maxchildren-1; 782 | 783 | my @tmp = split (/:/, $relayhost); 784 | $relayhost = $tmp[0]; 785 | if ( $tmp[1] ) { $relayport = $tmp[1]; } 786 | 787 | @tmp = split (/:/, $host); 788 | $host = $tmp[0]; 789 | if ( $tmp[1] ) { $port = $tmp[1]; } 790 | 791 | my $assassin = Mail::SpamAssassin->new({ 792 | 'dont_copy_prefs' => 1, 793 | 'debug' => $debug, 794 | 'local_tests_only' => $options{'local-only'} || 0 }); 795 | 796 | # 'stop_at_threshold' => $options{'stop_at_threshold'} || 0, 797 | 798 | $options{'auto-whitelist'} and eval { 799 | require Mail::SpamAssassin::DBBasedAddrList; 800 | 801 | # create a factory for the persistent address list 802 | my $addrlistfactory = Mail::SpamAssassin::DBBasedAddrList->new(); 803 | $assassin->set_persistent_address_list_factory ($addrlistfactory); 804 | }; 805 | 806 | $assassin->compile_now(); 807 | 808 | # thanks to Kurt Andersen for this fix for HPUX 809 | my $logsock = "unix"; 810 | eval { 811 | if (`uname -s` =~ 'HP-UX') { $logsock = "inet"; } 812 | }; 813 | 814 | my $server = bless { 815 | server => {host => $host, 816 | port => [ $port ], 817 | log_file => 'Sys::Syslog', 818 | syslog_logsock => $logsock, 819 | syslog_ident => 'spampd', 820 | syslog_facility => 'mail', 821 | background => 1, 822 | # setsid => 1, 823 | pid_file => $pidfile, 824 | user => $user, 825 | group => $group, 826 | max_servers => $children, 827 | max_requests => $maxrequests, 828 | # min_servers => $children, 829 | # max_servers => $maxchildren, 830 | # min_spare_servers => $min_spare_servers, 831 | # max_spare_servers => $max_spare_servers, 832 | }, 833 | spampd => { relayhost => $relayhost, 834 | relayport => $relayport, 835 | tagall => $tagall, 836 | maxsize => $maxsize, 837 | assassin => $assassin, 838 | childtimeout => $childtimeout, 839 | satimeout => $satimeout, 840 | rh => $rh, 841 | debug => $debug, 842 | dose => $dose, 843 | addheader => $addheader, 844 | myhostname => $myhostname, 845 | instance => 0, 846 | }, 847 | }, 'SpamPD'; 848 | 849 | # call Net::Server to start up the daemon inside 850 | $server->run; 851 | 852 | exit 1; # shouldn't get here 853 | 854 | sub usage { 855 | print < or B<--mc=n> C<(new in v2)> 925 | # 926 | # Maximum number of children to spawn if needed (where n >= --children). When 927 | # I starts it will spawn a number of child servers as specified by 928 | # --children. If all those servers become busy, a new child is spawned up to the 929 | # number specified in --maxchildren. Default is to have --maxchildren equal to 930 | # --children so extra child processes aren't started. Also see the --children 931 | # option, above. You may want to set your origination mail server to limit the 932 | # number of concurrent connections to I to match this setting (for 933 | # Postfix this is the C setting where 934 | # 'xxxx' is the transport being used, usually 'smtp', and the default is 100). 935 | # 936 | # Note that extra servers after the initial --children will only spawn on very 937 | # busy systems. This is because the check to see if a new server is needed (ie. 938 | # all current ones are busy) is only done around once per minute (this is 939 | # controlled by the Net::Server::PreFork module, in case you want to 940 | # hack at it :). It can still be useful as an "overflow valve," and is 941 | # especially nice since the extra child servers will die off once they're not 942 | # needed. 943 | 944 | =pod 945 | 946 | =head1 NAME 947 | 948 | SpamPD - Spam Proxy Daemon (version 2.10) 949 | 950 | =head1 Synopsis 951 | 952 | B 953 | [B<--host=host[:port]>] 954 | [B<--relayhost=hostname[:port]>] 955 | [B<--user|u=username>] 956 | [B<--group|g=groupname>] 957 | [B<--children|c=n>] 958 | #[B<--maxchildren|mc=n>] 959 | [B<--maxrequests=n>] 960 | [B<--childtimeout=n>] 961 | [B<--satimeout=n>] 962 | [B<--pid|p=filename>] 963 | [B<--maxsize=n>] 964 | [B<--dose>] 965 | [B<--tagall|a>] 966 | [B<--log-rules-hit|rh>] 967 | [B<--auto-whitelist|aw>] 968 | [B<--local-only|L>] 969 | [B<--debug|d>] 970 | 971 | B B<--help> 972 | 973 | =head1 Description 974 | 975 | I is an SMTP/LMTP proxy that marks (or tags) spam using 976 | SpamAssassin (http://www.SpamAssassin.org/). The proxy is designed 977 | to be transparent to the sending and receiving mail servers and at no point 978 | takes responsibility for the message itself. If a failure occurs within 979 | I (or SpamAssassin) then the mail servers will disconnect and the 980 | sending server is still responsible for retrying the message for as long 981 | as it is configured to do so. 982 | 983 | I uses SpamAssassin to modify (tag) relayed messages based on 984 | their spam score, so all SA settings apply. This is described in the SA 985 | documentation. I will by default only tell SA to tag a 986 | message if it exceeds the spam threshold score, however you can have 987 | it rewrite all messages passing through by adding the --tagall option 988 | (see SA for how non-spam messages are tagged). 989 | 990 | I logs all aspects of its operation to syslog(8), using the 991 | mail syslog facility. 992 | 993 | The latest version can be found at 994 | L. 995 | 996 | =head1 Requires 997 | 998 | =over 5 999 | 1000 | Perl modules: 1001 | 1002 | =item B 1003 | 1004 | =item B 1005 | 1006 | =item B 1007 | 1008 | =item B 1009 | 1010 | =item B (not actually required but recommended) 1011 | 1012 | =back 1013 | 1014 | =head1 Operation 1015 | 1016 | I is meant to operate as an S/LMTP mail proxy which passes 1017 | each message through SpamAssassin for analysis. Note that I 1018 | does not do anything other than check for spam, so it is not suitable as 1019 | an anti-relay system. It is meant to work in conjunction with your 1020 | regular mail system. Typically one would pipe any messages they wanted 1021 | scanned through I after initial acceptance by your MX host. 1022 | This is especially useful for using Postfix's (http://www.postfix.org) 1023 | advanced content filtering mechanism, although certainly not limited to 1024 | that application. 1025 | 1026 | Please re-read the second sentence in the above paragraph. You should NOT 1027 | enable I to listen on a public interface (IP address) unless you 1028 | know exactly what you're doing! It is very easy to set up an open relay this 1029 | way. 1030 | 1031 | Here are some simple examples (square brackets in the "diagrams" indicate 1032 | physical machines): 1033 | 1034 | 1035 | B 1036 | 1037 | =over 3 1038 | 1039 | The firewall/gateway MTA would be configured to forward all of its mail 1040 | to the port that I listens on, and I would relay its 1041 | messages to port 25 of your internal server. I could either 1042 | run on its own host (and listen on any port) or it could run on either 1043 | mail server (and listen on any port except port 25). 1044 | 1045 | Internet -> [ MX gateway (@inter.net.host:25) -> 1046 | spampd (@localhost:2025) ] -> 1047 | Internal mail (@private.host.ip:25) 1048 | 1049 | =back 1050 | 1051 | B 1052 | 1053 | =over 3 1054 | 1055 | Please see the F that came with the Postfix distribution. You 1056 | need to have a version of Postfix which supports this (ideally v.2 and up). 1057 | 1058 | Internet -> [ Postfix (@inter.net.host:25) -> 1059 | spampd (@localhost:10025) -> 1060 | Postfix (@localhost:10026) ] -> final delivery 1061 | 1062 | =back 1063 | 1064 | Note that these examples only show incoming mail delivery. Since it is 1065 | usually unnecessary to scan mail coming from your network (right?), 1066 | it may be desirable to set up a separate outbound route which bypasses 1067 | I. 1068 | 1069 | =head1 Upgrading 1070 | 1071 | Upgrading from version 1 simply involves replacing the F program file 1072 | with the latest one. Note that the I folder is no longer being 1073 | used and the --dead-letters option is no longer needed (though no errors are 1074 | thrown if it's present). Check the L<"Options"> list below for a full list of new 1075 | and deprecated options. Also be sure to check out the change log. 1076 | 1077 | =head1 Installation 1078 | 1079 | I can be run directly from the command prompt if desired. This is 1080 | useful for testing purposes, but for long term use you probably want to put 1081 | it somewhere like /usr/bin or /usr/local/bin and execute it at system startup. 1082 | For example on Red Hat-style Linux system one can use a script in 1083 | /etc/rc.d/init.d to start I (a sample script is available on the 1084 | I Web page @ http://www.WorldDesign.com/index.cfm/rd/mta/spampd.htm). 1085 | 1086 | The options all have reasonable defaults, especially for a Postfix-centric 1087 | installation. You may want to specify the --children option if you have an 1088 | especially beefy or weak server box because I is a memory-hungry 1089 | program. Check the L<"Options"> for details on this and all other parameters. 1090 | 1091 | Note that I B I from the I distribution 1092 | in function. You do not need to run I in order for I to work. 1093 | This has apparently been the source of some confusion, so now you know. 1094 | 1095 | =head2 Postfix-specific Notes 1096 | 1097 | Here is a typical setup for Postfix "advanced" content filtering as described 1098 | in the F that came with the Postfix distribution (which you 1099 | really need to read): 1100 | 1101 | F: 1102 | 1103 | smtp inet n - y - - smtpd 1104 | -o content_filter=smtp:localhost:10025 1105 | -o myhostname=mx.example.com 1106 | 1107 | localhost:10026 inet n - n - 10 smtpd 1108 | -o content_filter= 1109 | -o myhostname=mx-int.example.com 1110 | 1111 | The first entry is the main public-facing MTA which uses localhost:10025 1112 | as the content filter for all mail. The second entry receives mail from 1113 | the content filter and does final delivery. Both smtpd instances use 1114 | the same Postfix F file. I is the process that listens on 1115 | localhost:10025 and then connects to the Postfix listener on localhost:10026. 1116 | Note that the C options must be different between the two instances, 1117 | otherwise Postfix will think it's talking to itself and abort sending. 1118 | 1119 | For the above example you can simply start I like this: 1120 | 1121 | spampd --host=localhost:10025 --relayhost=localhost:10026 1122 | 1123 | F from the Postfix distro has more details and examples of 1124 | various setups, including how to skip the content filter for outbound mail. 1125 | 1126 | Another tip for Postfix when considering what timeout values to use for 1127 | --childtimout and --satimeout options is the following command: 1128 | 1129 | C<# postconf | grep timeout> 1130 | 1131 | This will return a list of useful timeout settings and their values. For 1132 | explanations see the relevant C page (smtp, smtpd, lmtp). By default 1133 | I is set up for the default Postfix timeout values. 1134 | 1135 | =head1 Options 1136 | 1137 | =over 5 1138 | 1139 | =item B<--host=ip[:port] or hostname[:port]> C<(changed in v2)> 1140 | 1141 | Specifies what hostname/IP and port I listens on. By default, it listens 1142 | on 127.0.0.1 (localhost) on port 10025. 1143 | 1144 | B You should NOT enable I to listen on a 1145 | public interface (IP address) unless you know exactly what you're doing! 1146 | 1147 | =item B<--port=n> 1148 | 1149 | Specifies what port I listens on. By default, it listens on 1150 | port 10025. This is an alternate to using the above --host=ip:port notation. 1151 | 1152 | =item B<--relayhost=ip[:port] or hostname[:port]> 1153 | 1154 | Specifies the hostname/IP where I will relay all 1155 | messages. Defaults to 127.0.0.1 (localhost). If the port is not provided, that 1156 | defaults to 25. 1157 | 1158 | =item B<--relayport=n> C<(new in v2)> 1159 | 1160 | Specifies what port I will relay to. Default is 25. This is an 1161 | alternate to using the above --relayhost=ip:port notation. 1162 | 1163 | =item B<--user=username> or B<--u=username> 1164 | 1165 | =item B<--group=groupname> or B<--g=groupname> 1166 | 1167 | Specifies the user and group that the proxy will run as. Default is 1168 | I/I. 1169 | 1170 | =item B<--children=n> or B<--c=n> C<(new in v2)> 1171 | 1172 | Number of child servers to start and maintain (where n > 0). Each child will 1173 | process up to --maxrequests (below) before exiting and being replaced by 1174 | another child. Keep this number low on systems w/out a lot of memory. 1175 | Default is 5 (which seems OK on a 512MB lightly loaded system). Note that 1176 | there is always a parent process running, so if you specify 5 children you 1177 | will actually have 6 I processes running. 1178 | 1179 | You may want to set your origination mail server to limit the 1180 | number of concurrent connections to I to match this setting (for 1181 | Postfix this is the C setting where 1182 | 'xxxx' is the transport being used, usually 'smtp', and the default is 100). 1183 | 1184 | =item B<--maxrequests=n> 1185 | 1186 | I works by forking child servers to handle each message. The 1187 | B parameter specifies how many requests will be handled 1188 | before the child exits. Since a child never gives back memory, a large 1189 | message can cause it to become quite bloated; the only way to reclaim 1190 | the memory is for the child to exit. The default is 20. 1191 | 1192 | =item B<--childtimeout=n> C<(new in v2)> 1193 | 1194 | This is the number of seconds to allow each child server before it times out 1195 | a transaction. In an S/LMTP transaction the timer is reset for every command. 1196 | This timeout includes time it would take to send the message data, so it should 1197 | not be too short. Note that it's more likely the origination or destination 1198 | mail servers will timeout first, which is fine. This is just a "sane" failsafe. 1199 | Default is 360 seconds (6 minutes). 1200 | 1201 | =item B<--satimeout=n> C<(new in v2)> 1202 | 1203 | This is the number of seconds to allow for processing a message with 1204 | SpamAssassin (including feeding it the message, analyzing it, and adding 1205 | the headers/report if necessary). 1206 | This should be less than your origination and destination servers' timeout 1207 | settings for the DATA command. For Postfix the default is 300 seconds in both 1208 | cases (smtp_data_done_timeout and smtpd_timeout). In the event of timeout 1209 | while processing the message, the problem is logged and the message is passed 1210 | on anyway (w/out spam tagging, obviously). To fail the message with a temp 1211 | 450 error, see the --dose (die-on-sa-errors) option, below. 1212 | Default is 285 seconds. 1213 | 1214 | =item B<--pid=filename> or B<--p=filename> 1215 | 1216 | Specifies a filename where I will write its process ID so 1217 | that it is easy to kill it later. The directory that will contain this 1218 | file must be writable by the I user. The default is 1219 | F. 1220 | 1221 | =item B<--maxsize=n> 1222 | 1223 | The maximum message size to send to SpamAssassin, in KBytes. By default messages 1224 | over 64KB are not scanned at all, and an appropriate message is logged 1225 | indicating this. The size includes headers and attachments (if any). 1226 | 1227 | =item B<--dose> C<(new in v2)> 1228 | 1229 | Acronym for (d)ie (o)n (s)pamAssassin (e)rrors. By default if I 1230 | encounters a problem with processing the message through Spam Assassin (timeout 1231 | or other error), it will still pass the mail on to the destination server. If 1232 | you specify this option however, the mail is instead rejected with a temporary 1233 | error (code 450, which means the origination server should keep retrying to send 1234 | it). See the related --satimeout option, above. 1235 | 1236 | =item B<--tagall> or B<--a> 1237 | 1238 | Tells I to have SpamAssassin add headers to all scanned mail, 1239 | not just spam. By default I will only rewrite messages which 1240 | exceed the spam threshold score (as defined in the SA settings). Note that 1241 | for this option to work as of SA-2.50, the I and/or 1242 | I settings in your SpamAssassin F need to be 1243 | set to 1/true. 1244 | 1245 | =item B<--log-rules-hit> or B<--rh> C<(new in v2)> 1246 | 1247 | Logs the names of each SpamAssassin rule which matched the message being 1248 | processed. This list is returned by SA. 1249 | 1250 | =item B<--add-sc-header> or B<--ash> C<(new in v2.1)> 1251 | 1252 | Add a 'X-Spam-Checked-By: {hostname}' header to each scanned message. By 1253 | default no such header is added. This can be useful in tracking which server 1254 | in a pool did the scanning. See below for how to specify a hostname. 1255 | 1256 | =item B<--hostname=hostname> C<(new in v2.1)> 1257 | 1258 | Hostname to use in the X-Spam-Checked-By header. By default the value of the 1259 | environmental variable $HOSTNAME is used, or if that is undefined/blank then 1260 | 'localhost' is used as the hostname. Only relevant if the --add-sc-header 1261 | option is specified. 1262 | 1263 | =item B<--auto-whitelist> or B<--aw> 1264 | 1265 | Turns on the SpamAssassin global whitelist feature. See the SA docs. Note 1266 | that per-user whitelists are not available. 1267 | 1268 | =item B<--local-only> or B<--L> C<(new in v2)> 1269 | 1270 | Turn off all SA network-based tests (DNS, Razor, etc). 1271 | 1272 | =item B<--debug> or B<--d> C<(changed in v2)> 1273 | 1274 | Turns on SpamAssassin debug messages which print to STDERR (usually the 1275 | console). Also turns on more verbose logging of what spampd is doing (new in 1276 | v2). 1277 | 1278 | =item B<--help> or B<--h> 1279 | 1280 | Prints usage information. 1281 | 1282 | =back 1283 | 1284 | =head2 Deprecated Options 1285 | 1286 | =over 5 1287 | 1288 | The following options are no longer used but still accepted for backwards 1289 | compatibility with I v1: 1290 | 1291 | =item B<--dead-letters> 1292 | 1293 | =item B<--heloname> 1294 | 1295 | =item B<--stop-at-threshold> 1296 | 1297 | =back 1298 | 1299 | =head1 Examples 1300 | 1301 | =over 5 1302 | 1303 | =item Running between firewall/gateway and internal mail server 1304 | 1305 | 1306 | I listens on port 10025 on the same host as the internal mail server. 1307 | 1308 | spampd --host=192.168.1.10 1309 | 1310 | Same as above but I runs on port 10025 of the same host as 1311 | the firewall/gateway and passes messages on to the internal mail server 1312 | on another host. 1313 | 1314 | spampd --relayhost=192.168.1.10 1315 | 1316 | =item Using Postfix advanced content filtering example 1317 | and the SA auto-whitelist feature 1318 | 1319 | spampd --port=10025 --relayhost=127.0.0.1:10026 --auto-whitelist 1320 | 1321 | =back 1322 | 1323 | =head1 Credits 1324 | 1325 | I is written and maintained by Maxim Paperno . 1326 | See http://www.WorldDesign.com/index.cfm/rd/mta/spampd.htm for latest info. 1327 | 1328 | I v2 uses two Perl modules by Bennett Todd and Copyright (C) 2001 Morgan 1329 | Stanley Dean Witter. These are distributed under the GNU GPL (see 1330 | module code for more details). Both modules have been slightly modified 1331 | from the originals and are included in this file under new names. 1332 | 1333 | Also thanks to Bennet Todd for the example smtpproxy script which helped create 1334 | this version of I. See http://bent.latency.net/smtpprox/ . 1335 | 1336 | I v1 was based on code by Dave Carrigan named I. Trace 1337 | amounts of his code or documentation may still remain. Thanks to him for the 1338 | original inspiration and code. See http://www.rudedog.org/assassind/ . 1339 | 1340 | Also thanks to I (included with SpamAssassin) and 1341 | I (http://www.ijs.si/software/amavisd/) for some tricks. 1342 | 1343 | =head1 Copyright, License, and Disclaimer 1344 | 1345 | I is Copyright (c) 2002 by World Design Group and Maxim Paperno. 1346 | 1347 | Portions are Copyright (C) 2001 Morgan Stanley Dean Witter as mentioned above 1348 | in the Credits section. 1349 | 1350 | This program is free software; you can redistribute it and/or modify 1351 | it under the terms of the GNU General Public License as published by 1352 | the Free Software Foundation; either version 2 of the License, or 1353 | (at your option) any later version. 1354 | 1355 | This program is distributed in the hope that it will be useful, 1356 | but WITHOUT ANY WARRANTY; without even the implied warranty of 1357 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1358 | GNU General Public License for more details. 1359 | 1360 | The GNU GPL can be found at http://www.fsf.org/copyleft/gpl.html 1361 | 1362 | 1363 | =head1 Bugs 1364 | 1365 | None known. Please report any to MPaperno@WorldDesign.com. 1366 | 1367 | =head1 To Do 1368 | 1369 | Figure out how to use Net::Server::PreFork because it has cool potential for 1370 | load management. I tried but either I'm missing something or PreFork is 1371 | somewhat broken in how it works. If anyone has experience here, please let 1372 | me know. 1373 | 1374 | Add configurable option for rejecting mail outright based on spam score. 1375 | It would be nice to make this program safe enough to sit in front of a mail 1376 | server such as Postfix and be able to reject mail before it enters our systems. 1377 | The only real problem is that Postfix will see localhost as the connecting 1378 | client, so that disables any client-based checks Postfix can do and creates a 1379 | possible relay hole if localhost is trusted. 1380 | 1381 | Per-user preferences: The jury is still out on this one. I'm thinking more 1382 | and more that most per-user prefs should be specified on the final mailbox 1383 | server. Why? Because SMTP isn't designed with per-user preferences in mind. 1384 | On a relay server, the same message body can go to multiple recipients who 1385 | may have wildly different preferences when it comes to handilng junk mail. The 1386 | exception here might be the use of LMTP protocol, which bears further 1387 | investigation. 1388 | 1389 | =head1 See Also 1390 | 1391 | perl(1), Spam::Assassin(3), L, 1392 | L 1393 | -------------------------------------------------------------------------------- /previous-versions/spampd.1.0.1.pl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpaperno/spampd/b023c3b5ca372060904faa7cc051902430d05a02/previous-versions/spampd.1.0.1.pl -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/mpaperno/spampd?include_prereleases)](https://github.com/mpaperno/spampd/releases) 2 | [![Documentation](https://img.shields.io/badge/POD-Documentation-39457E?logo=perl)](https://github.com/mpaperno/spampd/blob/master/spampd.pod) 3 | [![GPLv3 License](https://img.shields.io/badge/license-GPLv3-blue.svg)](LICENSE.txt) 4 | 5 | # SpamPD - Spam Proxy Daemon 6 | 7 | Originally released in May of 2002, _SpamPD_ is a program used within an e-mail delivery system to scan messages for possible Unsolicited Commercial E-mail (UCE, aka spam) content. 8 | It uses an excellent program called SpamAssassin (SA) to do the actual message scanning. SpamPD acts as a transparent SMTP/LMTP proxy between 9 | two mail servers, and during the transaction it passes the mail through SA. If SA decides the mail could be spam, then SpamPD will ask SA to 10 | add some headers and a report to the message indicating it's spam and why. 11 | 12 | SpamPD is written in Perl and should theoretically run on any platform supported by Perl and SpamAssassin. 13 | 14 | Here's an un-solicited comment someone sent regarding *SpamPD* performance: 15 | 16 | > Just to let you know: We have the SA/spampd combo up an running in a high volume environment. With 3 KAT-B Server (4x 2,5 GHz Xeon MP 17 | with Hyperthreading, 3 GB RAM) we handle 15.000 to 20.000 Mails/h (Hour!) with room to spare. We had some performance issues with the 18 | Bayes databases but now everything runs smoothly. 19 | 20 | Check the [Releases](https://github.com/mpaperno/spampd/releases) area for latest versions, 21 | and see the "previous-versions" folder for some more ancient ones.
22 | (Note that the Debian package version was added to this repo as a branch, and those tags will also show up in the Releases page.) 23 | 24 | Please read the [POD file](https://github.com/mpaperno/spampd/blob/master/spampd.pod) for full documentation of the many available options. 25 | See the [changelog](https://github.com/mpaperno/spampd/blob/master/changelog.txt) for full version history. 26 | 27 | ## Package status 28 | 29 | 30 | Packaging status 31 | 32 | 33 | **HELP!** Debian package maintainer needed. Please see [GitHub Issue 46](https://github.com/mpaperno/spampd/issues/46). 34 | 35 | Linux packages data at right courtesy of Repology: 36 | 37 | 38 |

Usage

39 |

SpamPD was initially designed as a content filter mechanism for use with the Postfix MTA. 40 | However, it has no inherent dependencies on Postfix or any other MTA. 41 | Some more specific setup information is provided in the included documentation.

42 | 43 |

Version 2 Architecture

44 |

Version 2 of SpamPD is a major rewrite of the underlying methods. SpamPD 45 | no longer acts as a relay server but more as a "transparent" proxy 46 | server. That is, it never actually takes responsibility for the mail at 47 | any point. Instead, the origination and destination mail servers speak 48 | directly to each other. If a failure occurs within SpamPD 49 | (or SpamAssassin) during a transaction, then the mail servers will 50 | disconnect and the sending server is still responsible for retrying the 51 | message for as long as it is configured to do so. Responsibility for 52 | mail delivery always lies with the 2 mail servers, which would be "real" 53 | MTAs and not a 500 line Perl script :-) This removes a major problem 54 | with version 1 of SpamPD, and makes this a recommended upgrade.

55 |

While this is a much safer technique than previously employed, it does remove a possible feature which some users of SpamPD 56 | have implemented (sorry guys). That is redirecting spam to a spamtrap 57 | address instead of letting the message through to the original 58 | recipient. This is due to the fact that the recipient information is 59 | passed on to the destination server before the message data is scanned 60 | for spam. On the other hand it presents the possibility of rejecting 61 | spam at the S/LMTP level without having to generate bounce notices and 62 | such.

63 | 64 |

SpamPD now fully supports the LMTP 65 | protocol (due to the nature of it's new transparency). Logging has been 66 | improved and is now more compatible with spamd. 67 | New parameters added: --children, --local-only, --childtimeout, 68 | --satimeout, --dose, --log-rules-hit, --add-sc-header, and --hostname. 69 | Three parameters are now deprecated: --dead-letters, --heloname, and 70 | --stop-at-threshold.

71 |

More details and further changes are documented in the change log.

72 | 73 |

More Information

74 | 75 |

If you aren't familiar with SpamAssassin, 76 | then you should definitely start there (or end up there) first. There 77 | is a very helpful users discussion list for SA (see their site). For Postfix setup, be sure to read the FILTER_README document that is included with the distribution. SpamPD is meant to be used as an "advanced content filtering" method (some examples are included with the SpamPD 78 | documentation). Postfix also has a helpful users discussion list. Make 79 | sure you do your homework before you ask other people to help you!

80 | 81 |

Be sure to check out the SpamPD documentation, the change log, as well as comments in the actual code.

82 | 83 |

Credits

84 |

SpamPD is written and maintained by Maxim Paperno (https://github.com/mpaperno).

85 |

SpamPD contains code written by 86 | Bennecode Todd (Copyright (C) 2001 Morgan Stanley Dean Witter) and is used 87 | in accordance with the GNU General Public License. The code is in the 88 | form of two Perl modules which have been included in the program. Also 89 | his smtpproxy example program served as inspiration for this version of SpamPD.

90 |

SpamPD version 1 was based on code by Dave Carrigan named assassind. Trace amounts of his code or documentation may still remain. Thanks to him for the original inspiration and code.

91 |

Various people have contributed patches, bug reports, and ideas, all 92 | of whom I would like to thank. I have tried to include credits in code 93 | comments, documentation, and in the change log, as appropriate.

94 | 95 |

Copyright, License, & Disclaimer

96 |

Copyright Maxim Paperno; All rights reserved.

97 |

Portions are Copyright © 2001 Morgan Stanley Dean Witter as mentioned above in the CREDITS section.

98 |

This program is free software; you can redistribute it and/or modify 99 | it under the terms of the GNU General Public License as published by the 100 | Free Software Foundation; either version 2 of the License, or (at your 101 | option) any later version.

102 |

This program is distributed in the hope that it will be useful, but 103 | WITHOUT ANY WARRANTY; without even the implied warranty of 104 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 105 | Public License for more details.

106 |

The GNU GPL can be found at https://www.gnu.org/licenses/

107 | -------------------------------------------------------------------------------- /spampd.pod: -------------------------------------------------------------------------------- 1 | =encoding UTF-8 2 | 3 | =head1 NAME 4 | 5 | SpamPD - Spam Proxy Daemon 6 | 7 | =head1 VERSION 8 | 9 | Documentation for SpamPD version 2.62. 10 | 11 | 12 | =head1 DESCRIPTION 13 | 14 | I is an SMTP/LMTP proxy that marks (or tags) spam using 15 | SpamAssassin (L). The proxy is designed 16 | to be transparent to the sending and receiving mail servers and at no point 17 | takes responsibility for the message itself. If a failure occurs within 18 | I (or SpamAssassin) then the mail servers will disconnect and the 19 | sending server is still responsible for retrying the message for as long 20 | as it is configured to do so. 21 | 22 | I uses SpamAssassin to modify (tag) relayed messages based on 23 | their spam score, so all SA settings apply. This is described in the SA 24 | documentation. I will by default only tell SA to tag a 25 | message if it exceeds the spam threshold score, however you can have 26 | it rewrite all messages passing through by adding the --tagall option 27 | (see SA for how non-spam messages are tagged). 28 | 29 | I logs all aspects of its operation to syslog(8), using the 30 | mail syslog facility. 31 | 32 | The latest version can be found at L. 33 | 34 | 35 | =head1 SYNOPSIS 36 | 37 | B I<[ options ]> 38 | 39 | Options: 40 | 41 | --config Load options from file(s). 42 | 43 | --host [:] Hostname/IP and optional port to listen on. 44 | --port Port to listen on (alternate syntax to above). 45 | --socket UNIX socket to listen on. 46 | --socket-perms The octal mode to set on the UNIX socket. 47 | --relayhost [:] Host and optional port to relay mail to. 48 | --relayport Port to relay to (alternate syntax to above). 49 | --relaysocket UNIX socket to relay to. 50 | 51 | --min-servers | -mns The minimum number of servers to keep running. 52 | --min-spare | -mnsp The minimum number of servers to have waiting. 53 | --max-spare | -mxsp The maximum number of servers to have waiting. 54 | --max-servers | -mxs The maximum number of child servers to start. 55 | --maxrequests or -r Maximum requests that each child can process. 56 | --childtimeout Time out children after this many seconds. 57 | --satimeout Time out SpamAssassin after this many seconds. 58 | --child-name-template [s] Template for formatting child process name. 59 | 60 | --pid or -p Store the daemon's process ID in this file. 61 | --user or -u Specifies the user that the daemon runs as. 62 | --group or -g Specifies the group that the daemon runs as. 63 | 64 | --[no]detach Detach from the console daemonize (default). 65 | --[no]setsid Completely detach from stderr with setsid(). 66 | 67 | --maxsize n Maximum size of mail to scan (in KB). 68 | --dose (D)ie (o)n (s)pamAssassin (e)rrors. 69 | --tagall Tag all messages with SA headers, not just spam. 70 | --set-envelope-headers Set X-Envelope-From and X-Envelope-To headers. 71 | --set-envelope-from Set X-Envelope-From header only. 72 | 73 | --local-only or -L Turn off all SA network-based tests. 74 | --homedir Use the specified directory as SA home. 75 | --saconfig Use the file for SA "user_prefs" configuration. 76 | 77 | --logfile or -o Destination for logs (syslog|stderr|). 78 | --logsock or -ls Allows specifying the syslog socket type. 79 | --logident or -li Specify syslog identity name. 80 | --logfacility or -lf Specify syslog facility (log name). 81 | --log-rules-hit or -rh Log the names of each matched SA test per mail. 82 | --debug or -d [] Controls extra debug logging. 83 | 84 | --help | -h | -? [txt] Show basic command-line usage. 85 | -hh | -?? [txt] Show short option descriptions (this text). 86 | -hhh | -??? [txt] Show usage summary and full option descriptions. 87 | --man [html|txt] Show full docs a man page or HTML/plain text. 88 | --show defaults| Print default option values or and exit. 89 | --version Print version information and exit. 90 | 91 | Compatibility with previous SpamPD versions: 92 | 93 | --children or -c Same as --max-servers | -mxs (since v2.60). 94 | 95 | Deprecated since SpamAssassin v3: 96 | 97 | --auto-whitelist or -aw Use the SA global auto-whitelist feature. 98 | 99 | 100 | =head1 REQUIRES 101 | 102 | Perl modules: 103 | 104 | =over 5 105 | 106 | =item B 107 | 108 | =item B (>= v0.89, v2.009+ recommended) with B and/or B submodules. 109 | 110 | =item B 111 | 112 | =item B 113 | 114 | =item B (if using TCP/IP sockets) 115 | 116 | =item B (if using UNIX sockets) 117 | 118 | =back 119 | 120 | =head1 OPERATION 121 | 122 | I is meant to operate as an S/LMTP mail proxy which passes 123 | each message through SpamAssassin for analysis. Note that I 124 | does not do anything other than check for spam, so it is not suitable as 125 | an anti-relay system. It is meant to work in conjunction with your 126 | regular mail system. Typically one would pipe any messages they wanted 127 | scanned through I after initial acceptance by your MX host. 128 | This is especially useful for using Postfix's (http://www.postfix.org) 129 | advanced content filtering mechanism, although certainly not limited to 130 | that application. 131 | 132 | Please re-read the second sentence in the above paragraph. You should NOT 133 | enable I to listen on a public interface (IP address) unless you 134 | know exactly what you're doing! It is very easy to set up an open relay this 135 | way. 136 | 137 | Here are some simple examples (square brackets in the "diagrams" indicate 138 | physical machines): 139 | 140 | =over 2 141 | 142 | =item B 143 | 144 | The firewall/gateway MTA would be configured to forward all of its mail 145 | to the port that I listens on, and I would relay its 146 | messages to port 25 of your internal server. I could either 147 | run on its own host (and listen on any port) or it could run on either 148 | mail server (and listen on any port except port 25). 149 | 150 | Internet -> 151 | [ MX gateway (@inter.net.host:25) -> spampd (@localhost:2025) ] -> 152 | [ Internal mail (@private.host.ip:25) ] 153 | 154 | =item B 155 | 156 | Please see the F that came with the Postfix distribution. You 157 | need to have a version of Postfix which supports this (ideally v.2 and up). 158 | 159 | Internet -> [ Postfix (@inter.net.host:25) -> 160 | spampd (@localhost:10025) -> 161 | Postfix (@localhost:10026) ] -> final delivery 162 | 163 | =back 164 | 165 | Note that these examples only show incoming mail delivery. Since it is 166 | often unnecessary to scan mail coming from your network, it may be desirable 167 | to set up a separate outbound route which bypasses I. 168 | 169 | =head2 Scalable Mode 170 | 171 | Since v2.60 I can optionally run in "scalable mode" which dynamically adjusts the number 172 | of child servers which can process requests simultaneously. This is activated automatically if the 173 | C<--min-servers> option is specifically set to be lower than C<--max-servers>. 174 | 175 | Historically I inherited from the module I which only allows for 176 | a static number of child servers to be running at once. This new option essentially allows for inheriting from 177 | I which features dynamic allocation of child servers, with some tunable parameters. 178 | (The reason I wasn't used to begin with is because older versions of it didn't seem to work... 179 | it was an old TODO to try again later.) 180 | 181 | Here is what the I documentation has to say (option names changed to match I): 182 | 183 | I<"This personality binds to one or more ports and then forks C<--min-servers> child process. The server 184 | will make sure that at any given time there are C<--min-spare> servers available to receive a client 185 | request, up to C<--max-servers>. Each of these children will process up to C<--maxrequests> client 186 | connections. This type is good for a heavily hit site, and should scale well for most applications."> 187 | 188 | Some experimentation and tuning will likely be needed to get the best performance vs. efficiency. Keep in mind 189 | that a SIGHUP sent to the parent process will reload configuration files and restart child servers gracefully 190 | (handy for tuning a busy site). 191 | 192 | See the documentation for C<--min-servers>, C<--max-servers>, C<--min-spare>, and C<--max-spare> options, 193 | and also the section about L for tuning parameters and links to further documentation. 194 | 195 | 196 | =head1 INSTALLATION AND CONFIGURATION 197 | 198 | I can be run directly from the command prompt if desired. This is 199 | useful for testing purposes, but for long term use you probably want to put 200 | it somewhere like /usr/bin or /usr/local/bin and execute it at system startup. 201 | For example on Red Hat-style Linux system one can use a script in 202 | /etc/rc.d/init.d to start I (a L 203 | is available in the I code repository). 204 | 205 | I is available as a B for a significant number of Linux distributions, 206 | including Debian and derivatives (Ubuntu, etc). This is typically the easiest/best way 207 | to install and configure I since it should already take into account any system 208 | specifics for setting up and running as a daemon, etc. Note however that packages 209 | might not offer the latest version of I. A good reference for available 210 | packages and their versions can be found at L. 211 | 212 | I is also used in the turnkey L 213 | project, which includes Postfix as the main MTA and Dovecot as the local delivery agent 214 | with LMTP protocol. Even if you don't need the turnkey solution, it may be informative 215 | to peruse the MIAB L / 216 | L files for reference. 217 | 218 | All I options have reasonable defaults, especially for a Postfix-centric 219 | installation. You may want to specify the C<--max-servers> option if you have an 220 | especially beefy or weak server box because I is a memory-hungry 221 | program. Check the L<"Options"> for details on this and all other parameters. 222 | 223 | To show default values for all options, run C. 224 | 225 | B I injects a C<_SPAMPDVERSION_> 226 | L<"template tag"|https://spamassassin.apache.org/doc/Mail_SpamAssassin_Conf.html#TEMPLATE-TAGS> 227 | macro at message processing time. This can be used in an C SA config file directive, for example. 228 | 229 | add_header all Filter-Version SpamAssassin _VERSION_ (_SUBVERSION_, Rules: _RULESVERSION_) / SpamPD _SPAMPDVERSION_ 230 | 231 | Note that B< I replaces I > from the I distribution 232 | in function. You do not need to run I in order for I to work. 233 | This has apparently been the source of some confusion, so now you know. 234 | 235 | =head2 Postfix-specific Notes 236 | 237 | Here is a typical setup for Postfix "advanced" content filtering as described 238 | in the F that came with the Postfix distribution (which you 239 | really need to read): 240 | 241 | F: 242 | 243 | smtp inet n - y - - smtpd 244 | -o content_filter=smtp:localhost:10025 245 | -o myhostname=mx.example.com 246 | 247 | localhost:10026 inet n - n - 10 smtpd 248 | -o content_filter= 249 | -o myhostname=mx-int.example.com 250 | 251 | The first entry is the main public-facing MTA which uses localhost:10025 252 | as the content filter for all mail. The second entry receives mail from 253 | the content filter and does final delivery. Both smtpd instances use 254 | the same Postfix F file. I is the process that listens on 255 | localhost:10025 and then connects to the Postfix listener on localhost:10026. 256 | Note that the C options must be different between the two instances, 257 | otherwise Postfix will think it's talking to itself and abort sending. 258 | 259 | For the above example you can simply start I like this: 260 | 261 | spampd --host=localhost:10025 --relayhost=localhost:10026 262 | 263 | F from the Postfix distro has more details and examples of 264 | various setups, including how to skip the content filter for outbound mail. 265 | 266 | Another tip for Postfix when considering what timeout values to use for 267 | --childtimout and --satimeout options is the following command: 268 | 269 | C<# postconf | grep timeout> 270 | 271 | This will return a list of useful timeout settings and their values. For 272 | explanations see the relevant C page (smtp, smtpd, lmtp). By default 273 | I is set up for the default Postfix timeout values. 274 | 275 | The following guide has some more specific setup instructions: 276 | B> 277 | 278 | 279 | =head1 UPGRADING 280 | 281 | Always consult the F file which should be included in the I repository/distribution. 282 | 283 | If upgrading from a version B, please note that the --add-sc-header 284 | option is no longer supported. Use SA's built-in header manipulation features 285 | instead (as of SA v2.6). 286 | 287 | Upgrading from B simply involves replacing the F program file 288 | with the latest one. Note that the I folder is no longer being 289 | used and the --dead-letters option is no longer needed (though no errors are 290 | thrown if it's present). Check the L list below for a full list of new 291 | and deprecated options. Also be sure to check out the change log. 292 | 293 | B I has a new L feature which varies the number of running 294 | child servers based on demand. This is disabled by default. The option previosly known as 295 | C<--children> (or C<-c>) is now called C<--max-servers> (or C<-mxs>), but the old style is still accepted. 296 | See descriptions of the C and C options for details. 297 | 298 | Also note that v2.60 added the ability to use a L for specifying all options. 299 | 300 | 301 | =head1 USAGE 302 | 303 | spampd [ 304 | [ --config | --cfg | --config-file | --cfg-file [] ][...] 305 | 306 | [ --host [:] | --socket --socket-perms ] 307 | [ --relayhost [:] | --relaysocket ] 308 | 309 | [--min-servers | -mns ] [--saconfig ] [--user | -u ] 310 | [--min-spare | -mnsp ] [--satimeout ] [--group | -g ] 311 | [--max-spare | -mxsp ] [--dose ] [--pid | -p ] 312 | [--max-servers | -mxs ] [--maxsize ] [--[no]detach ] 313 | [--maxrequests | -r ] [--local-only | -L ] [--[no]setsid ] 314 | [--childtimeout ] [--tagall | -a ] [--log-rules-hit | -rh] 315 | [ --child-name-template | -cnt [