Compare commits

..

1096 Commits

Author SHA1 Message Date
Jay Lee
e6610357e3 Update README.md 2019-04-13 11:01:54 -04:00
Jay Lee
8ad2f8d633 MacOS now requires 10.13 High Sierra 2019-04-13 10:58:38 -04:00
Jay Lee
127d354972 GAM 4.70 2019-04-13 10:41:12 -04:00
josemdv
a099981b3c Removing Groups API properties that will be deprecated (#849) 2019-04-13 10:40:30 -04:00
Gustavo Murayama
857230def3 Translate cp65001 encoding to UTF-8 (#862) 2019-04-13 10:40:15 -04:00
Ross Scroggs
693c23e562 Fix bug in settings scopes (#884)
21 *->blank
21 blank->*
21r *-> R
21 R->blank
21 blank -> R Should be *
2019-04-12 17:27:57 -04:00
Jay Lee
5c6e31200e Use gam version in output 2019-04-12 16:54:25 -04:00
Jay Lee
074318d797 combine builds into single draft 2019-04-12 16:45:56 -04:00
Jay Lee
838b377d4b Fix .zip dist path 2019-04-12 16:40:05 -04:00
Jay Lee
f1061d7190 Windows .zip should only contain binary dist files 2019-04-12 16:26:20 -04:00
Jay Lee
948e40f51a First attempt at GitHub releases 2019-04-12 16:13:49 -04:00
Jay Lee
fb6746e694 disable 32-bit linux for now 2019-04-12 15:21:15 -04:00
Jay Lee
e68632e840 run gam earlier to see what we got 2019-04-12 14:41:55 -04:00
Jay Lee
049319c499 keep light.exe failure on ROOTDRIVE from faillng the build 2019-04-12 14:32:43 -04:00
Jay Lee
958c2ee356 comma 2019-04-12 14:11:05 -04:00
Jay Lee
6b569df751 WiX is looking for gam-setup.bat 2019-04-12 14:03:42 -04:00
Jay Lee
7587a58dc3 comma, comma, comma, chameleon... 2019-04-12 13:55:06 -04:00
Jay Lee
7ea3930975 Travis doesn't like comments 2019-04-12 13:47:46 -04:00
Jay Lee
cccaf5fe54 fixes for 32/64 MSI build 2019-04-12 13:44:31 -04:00
Jay Lee
932dbd74ee more variables 2019-04-12 13:28:08 -04:00
Jay Lee
4cbb2300df We'll use seperate builds per platform/arch 2019-04-12 13:21:12 -04:00
Jay Lee
ecb4efa352 Add .0 to Wix build 2019-04-12 13:03:36 -04:00
Jay Lee
03d7fe0407 Do we dare try 32-bit? Oh we dare... 2019-04-12 13:01:55 -04:00
Jay Lee
3f6dff1543 enough with the semicolons already! 2019-04-12 12:44:15 -04:00
Jay Lee
9857869799 separate python2 and wixtoolset so .NET has time to finish 2019-04-12 12:36:38 -04:00
Jay Lee
8879c5d8c7 need to set GAMVERSION, use Travis build for now 2019-04-12 12:30:49 -04:00
Jay Lee
7d1edd41c7 Semi-colons again 2019-04-12 12:22:24 -04:00
Jay Lee
970542eb7f semicolon, no set (to much junk) 2019-04-12 12:09:54 -04:00
Jay Lee
5270929b57 try preinstalling .Net 3.5 for WiX 2019-04-12 12:05:28 -04:00
Jay Lee
6543ead625 Try WiX also... 2019-04-12 11:14:31 -04:00
Jay Lee
817d618d08 More attempts to find 7-zip :=/ 2019-04-12 10:58:31 -04:00
Jay Lee
a0833393a8 just don't upgrade pip :-/ 2019-04-12 10:52:32 -04:00
Jay Lee
811a2db58b pip --user to avoid needing admin access 2019-04-12 10:48:15 -04:00
Jay Lee
8b9af97012 semicolons, semicolons... 2019-04-12 10:43:43 -04:00
Jay Lee
36b874d8a0 back to MacOS 10.13, fix Windows zip 2019-04-12 10:36:24 -04:00
Jay Lee
5680bc05b5 Hoping we don't need 32-bit Program Files... 2019-04-12 10:30:36 -04:00
Jay Lee
90b8751874 Windows uses 7-zip 2019-04-12 10:27:40 -04:00
Jay Lee
51712af78d Path is /Python27/ 2019-04-12 10:10:10 -04:00
Jay Lee
f5681cb746 Try /c as windows path, stop deploy since it only breaks us for now 2019-04-12 10:05:00 -04:00
Jay Lee
85d52a4d68 more fun with Windows git bash paths 2019-04-12 09:57:02 -04:00
Jay Lee
cec7f88d2b semicolons per windows statement 2019-04-12 09:48:05 -04:00
Jay Lee
4043bcab61 Try setting path on Windows 2019-04-12 09:44:56 -04:00
Jay Lee
1efa50aeab try more languages 2019-04-12 09:28:27 -04:00
Jay Lee
400fa08ebc lanaguage generic so we don't get ruby 2019-04-12 09:24:02 -04:00
Jay Lee
eda1a1de24 try Windows again 2019-04-12 09:22:28 -04:00
Jay Lee
31d8cd09dd ignore default python on osx 2019-04-12 09:11:48 -04:00
Jay Lee
91b3ddb489 back to MacOS 10.13 2019-04-12 09:02:40 -04:00
Jay Lee
2392d575c2 we don't care about default Python on MacOS 2019-04-12 08:56:30 -04:00
Jay Lee
8b47748df9 use a matrix for OSes 2019-04-12 08:50:18 -04:00
Jay Lee
7a9899f747 try MacOS 10.13, remove release 2019-04-12 07:42:15 -04:00
Jay Lee
2e5e784f4c more path stuff 2019-04-12 07:38:17 -04:00
Jay Lee
5b9d14e966 correct dist path 2019-04-12 07:35:11 -04:00
Jay Lee
5fb0b1b5ff try 2.7.15 2019-04-12 07:31:56 -04:00
Jay Lee
a08d95a742 Python 2.7.16, more path fixes 2019-04-12 07:30:01 -04:00
Jay Lee
64380cbd4d fix spec path 2019-04-12 07:10:38 -04:00
Jay Lee
837d98deaf try cd into src 2019-04-12 07:07:40 -04:00
Jay Lee
a63ac294db more stuff for travis 2019-04-12 07:02:31 -04:00
Jay Lee
d20a0d8455 install 2019-04-12 06:41:32 -04:00
Jay Lee
cd710e99c0 travis ci experiment 2019-04-12 06:37:32 -04:00
Jay Lee
1cedbc9423 one less quote 2019-04-12 06:06:48 -04:00
ejochman
2ce5915e70 Refactor scope selection menu (#882)
-Add flexibility to menu creation and feature customization
-Colorize menu error messages on supported platforms
-Add 'email' as a required scope for increased transparency to the user
-Improve readability of menu creation and operation
2019-04-12 06:04:16 -04:00
Ross Scroggs
e602d3fef0 Define MAX_GOOGLE_SHEET_CELLS in var.py (#881) 2019-04-11 14:34:23 -04:00
ejochman
c6e1e5c1cf Remove errant character corrupting client_secrets.json (#880)
* Revert patched google_auth_httplib2 and replace functionality by wrapping original library calls

* Wrap calls to google_auth_httplib2.Request__call__ to include a user-agent header.

* Fix bad dict key assignment syntax

* Add user agent header wrapper to requests handled by AuthorizedHttp

* Remove errant character corrupting client_secrets.json

Removes an errant '`' in the raw string output to client_secrets.json that was corrupting the json output. Also fixes the error message in getOAuthClientIDAndSecret() to properly format the output with the target filename.

* Replace missing request wrappers
2019-04-11 12:50:43 -04:00
Ross Scroggs
837bff58e7 Toss some more Google+, update V1_DISCOVERY_APIS (#878)
* Toss some more Google+

* Add drive3 to V1_DISCOVERY_APIS, sort list

* Update var.py
2019-04-09 16:29:07 -04:00
Ross Scroggs
01d50adce7 Cleanup setting discoveryServiceUrl (#877) 2019-04-08 19:15:43 -04:00
Ross Scroggs
05cbe1c6f3 Update GamCommands.txt and Improve error message in get drivefile (#875)
* Update GamCommands.txt

* Improve error message when trying to dowload files

* Update GamCommands.txt
2019-04-08 18:49:47 -04:00
Jay Lee
939bd8d9ab Merge branch 'master' of https://github.com/jay0lee/GAM 2019-04-05 13:06:21 -04:00
Jay Lee
204a689848 Use v2 Discovery API URL when possible, remove google+ code
All newer APIs support a v2 Discovery URL that is preferred.
They have a fallback v1 URL also but in some cases this fallback
discovery file doesn't have all APIs and methods. We will use
v2 for all APIs that support it.

Also remove some old GPlus commands that are deprecated.
2019-04-05 13:02:45 -04:00
Ross Scroggs
a852c7e5ca Groups settings documentation cleanup (#873) 2019-04-01 15:12:12 -04:00
Ross Scroggs
b3ad45f2cc Add gs_get_before_update to create group (#872)
Appease pylint
2019-04-01 10:38:49 -04:00
Jay Lee
9f988bb464 make group settings get before update optional. 2019-03-28 19:06:03 +00:00
Jay Lee
ce0ad0a3ea Better error messages on group settings update for invalid values. 2019-03-28 14:29:49 +00:00
Ross Scroggs
c5daf892c6 Make SKU aliases consistent, update docs for new group settings (#866)
* Make SKU aliases consistent

* Update documentation with new SKUs

* Update for new group settings
2019-03-25 15:31:48 -04:00
Jay Lee
bd484cbe41 Drive Enterprise and Enterprise for Education Student SKUs 2019-03-22 20:13:48 -04:00
Ross Scroggs
cbfb0a7310 Delete duplicated oauth2client library (#852) 2019-02-28 07:05:55 -05:00
ejochman
45027da057 Consolidate callGAPIpages() implementation and add docstrings on all GAPI execution-related methods (#851)
* Consolidate callGAPIpages() implementation and add docstrings on all GAPI execution-related methods

callGAPIpages was previously broken out into a few subroutines which contained a couple lines of code and weren't being used elsewhere in the code. The main "GAPI" execution methods were also missing documentation which made it hard to tell what they did and/or how to use their parameters.

* Fix oauth2client library name
2019-02-20 10:12:27 -05:00
Jay Lee
fb53f2ed0e Fix oauth2 revoke URI, new URL doesn't seem to work 2019-02-15 13:53:05 -05:00
Jay Lee
37e0e8942e 3P library updates 2019-02-14 14:11:35 -05:00
Ross Scroggs
ef86508bbb Add csvsheet <String> and targetname - to get drivefile (#842)
* Add csvsheet <String> and targetname - to get drivefile

Standarize id: and uid: processing

* Update var.py for Sheets API

* Handle revisionId in download of non Google files
2019-02-13 15:27:08 -05:00
Jay Lee
f6459e20f9 Initial Alert Center API work 2019-02-13 11:46:09 -05:00
Jay Lee
aeff5edaeb Update README.md 2019-01-24 16:05:17 -05:00
The Gitter Badger
d274d05336 Add Gitter badge (#844) 2019-01-24 16:04:10 -05:00
Ross Scroggs
207eb0990c Extend project commands (#841)
gam create project [<EmailAddress>] [<ProjectID>]
gam use project [<EmailAddress>] [<ProjectID>]
gam update project [<EmailAddress>] [gam|<ProjectID>|(filter <String>)]
gam delete project [<EmailAddress>] [gam|<ProjectID>|(filter <String>)]
gam show projects [<EmailAddress>] [all|gam|<ProjectID>|(filter <String>)]
gam print projects [<EmailAddress>] [all|gam|<ProjectID>|(filter <String>)] [todrive]
2019-01-18 11:38:41 -05:00
Jay Lee
1cbe8297aa add support for archiving user, no details avail on what it does yet, leave undocumented till then 2019-01-15 16:36:07 -05:00
Ross Scroggs
5b8fcebabd create project cleanup (#839)
* create project cleanu

Google now puts a leading \n on client_id and client_secret; handle them.

Update instructions,

* Fix instructions.

* Add comment explaining extra raw_input
2019-01-15 16:13:15 -05:00
Ross Scroggs
820f17ce74 Cleanup (#838)
pylint 2125/2150
Standardize create vault messages
2019-01-13 10:14:27 -05:00
Ross Scroggs
753ecd7244 Allow setting of Team Drive restrictions (#834)
* Allow setting of Team Drive restrictions

* Add asadmin to doUpdateTeamDrive
2019-01-05 20:45:56 -05:00
Ross Scroggs
df3ea385ee Updated gam report users to support new orgUnitID argument in Reports API (#833) 2019-01-02 15:43:34 -05:00
Ross Scroggs
eb041e9e65 Update documentation (#832) 2019-01-02 10:55:06 -05:00
Ross Scroggs
96eb2496e4 Documentation cleanup (#829)
* Correct documentation

* Document create datatransfer service list
2018-12-20 20:37:18 -05:00
bbadger86
cbe89c8c40 Added ability to transfer data for multiple application (drive, calendar, Google+) in a single API request (#826) 2018-12-18 12:44:39 -05:00
Jay Lee
4d893c4da1 Remove notification commands per https://gsuiteupdates.googleblog.com/2018/12/admin-console-notification-changes.html 2018-12-17 13:25:12 -05:00
Ross Scroggs
9dd0b135b9 Handle no dns.resolver (#822)
* Handle no dns.resolver

* Include python dns library
2018-11-29 11:01:03 -05:00
Ross Scroggs
5b0439ddb5 Import move cleanup (#821)
calendar.timegm(time.gmtime()) and int(time.time()) return the same value. import calendar can be droped as it caused a name conflict later on.

With import dns.resolver moved to the top, try except ImportError can be dropped.
2018-11-25 09:46:10 -05:00
Jay Lee
c3cb82a2de move most imports to top to make sure libraries exist early 2018-11-22 20:08:00 -05:00
Jay Lee
516f13bf48 Update gam-install.sh 2018-11-22 15:08:04 -05:00
Jay Lee
c3bf18cf5a Update gam-install.sh 2018-11-22 15:07:12 -05:00
Ross Scroggs
ece2d2943e Handle oauth error without a trap (#819) 2018-11-20 12:01:24 -05:00
Ross Scroggs
610dbd4dcf Add smtpMsa options to create sendas; update license commands (#814)
* Add smtpMsa options to create sendas

This allows sendas addresses outside of your domain

* Update licenses commands

Add gam show licenses
Add countsonly option to gam print licenses

Add allskus and gsuite selection options
2018-11-19 11:56:41 -05:00
Jay Lee
fe3c043d61 re-add a few explicit checks on sheets size 2018-10-28 17:03:26 -04:00
Ross Scroggs
83d8135722 One large, four small fixes (#813)
* One large, three small fixes

Gam update group update backwards compatibility
8551/8552

Handle cpuStatusReports, diskVolumeReports, systemRamFreeReports for CrOS devices
9451/10291, 11417/11622

Cod cleanup writeCSVfile
10360.10375

Fix indentation
12678

* Make cell_count computation explicit
2018-10-28 16:57:26 -04:00
Jay Lee
6caf3f2252 GAM 4.65 2018-10-27 20:23:17 -04:00
Ross Scroggs
b8331a3a4a Multiple small fixes (#800)
* Multiple small fixes

Allow mixed case when creating/updating/deleting alias: 362/378, 8068, 8622, 10162

Recode callGAPIpages for future benefits: 890/940

Handle out of range start_time end_time values in gam report: 1441/1444

Work around API bug where primary can't be used as calendarId: 2979/2980

Code cleanup in doCalendarAddEvent: 3507/3600

Get integers with subroutine: 228-236, multiple calls

Minimize data download in doDeleteGuardian: 2330/2348

Add contentmanager/fileorganizer role: 3984/4074

* Fix typos

* Code cleanup calendar ACL commands; add sendnotifications

* Correct documentation error

* Add operatingSystemType to user posix attribute

* Add Jay's changes

group member delivery settings, check max sheet bytes on CSV upload

* Cleanup writeCSVfile sheet size checking

* Revert "Add Jay's changes"

This reverts commit 9eb90ba7d7.

* Revert "Cleanup writeCSVfile sheet size checking"

This reverts commit 139a2f7f4c.

* More reverting
2018-10-27 20:17:02 -04:00
Jay Lee
c271349c30 Merge branch 'master' of https://github.com/jay0lee/GAM 2018-10-22 10:22:14 -04:00
Jay Lee
329a6e0768 group member delivery settings, check max sheet bytes on CSV upload 2018-10-22 10:22:02 -04:00
Roman Hargrave
fae2dca9dc Add operatingSystemType to posix config (#808)
* Add operatingSystemType to posix config

* Downcase operatingsystemtype
2018-10-15 12:23:58 -04:00
Ross Scroggs
7112a42c96 Simplify setup dialog when upgrading; fix typos (#789) 2018-09-12 09:20:03 -04:00
Jan Almeroth
d76618cbad If language missing, default to en. Fixes #787 (#788)
* If language missing, default to en. Fixes #787

* lets make it clear that it was unset
2018-08-29 08:52:12 -04:00
Jay Lee
d8db37786c remove email settings from spec files 2018-08-27 18:59:10 -04:00
Jay Lee
79bc1065f3 GAM 4.61 2018-08-27 18:44:52 -04:00
Ross Scroggs
b107afc13c Add ability to specify suspended/not suspended users for groups and ous (#786)
Simplify specifying ChromeBooks by serial number
2018-08-27 18:26:06 -04:00
Jay Lee
fcfbf0a733 make create/delete delegate API calls soft error 2018-08-27 12:49:14 -04:00
Ross Scroggs
8460a7a87d Remove ability to promote/demote super admins; minor delegate cleanup (#785) 2018-08-27 12:47:58 -04:00
Ross Scroggs
2940dd71ab Fix query in update/rename labels, allow user to keep old label (#783)
* Fix query in update/rename labels, allow user to keep old label

* Drop keepoldlabel
2018-08-25 07:32:02 -04:00
Jay Lee
135ebaa251 handle non-Gmail users, counting 2018-08-24 08:37:30 -04:00
Jay Lee
f94b3eb383 remove email-settings JSON, fix e on scope selection 2018-08-24 08:28:23 -04:00
Jay Lee
a3db496f31 New Delegation API, yippee! 2018-08-24 08:21:27 -04:00
Ross Scroggs
dd78c05d59 Vault zip files were being extracted to cwd not toFolder (#781) 2018-08-20 21:01:09 -04:00
Ross Scroggs
d9c4326a6b Vault updates - Ignore until end of vacation (#777)
* Vault updates

Code cleanup
Add delete export/print export
Make start/startime end/endtime consistent
Add noverify noextract targetfolder to download export

* Avoid API error

* Fix error

* Fix timezone BNF
2018-08-13 17:38:32 -04:00
Jay Lee
4ec90dbcfe version bump. document flush 2018-08-03 20:30:31 +00:00
Ross Scroggs
b8c6800b37 Use openFile/closeFile to avoid traps (#776)
* Use openFile/closeFile to avoid traps

This code isn't necessary, it will happen as part of close
f.flush()
os.fsync(f.fileno())

* Put flush/sync back
2018-08-03 15:57:23 -04:00
Ross Scroggs
a82a33996c Code cleanup (#775) 2018-08-03 15:22:38 -04:00
Ross Scroggs
9a27f19e2e Code cleanup (#773)
* Code cleanup

* Cleanup gedtting matter name/id

* versionDate is full timestamp
2018-08-03 12:19:24 -04:00
Jay Lee
c56c6e3e05 Merge branch 'master' of https://github.com/jay0lee/GAM 2018-08-03 13:19:50 +00:00
Jay Lee
0c0fb37b33 semi-working gam create export 2018-08-03 13:19:15 +00:00
Ross Scroggs
e865d81dad Fix my mistake, keep pylint happy, set export folder (#771)
* Fix my mistake, keep pylint happy, set export folder

5439/5440: copy/paste mistake

7531/7550: file and object are Python objects, pylint gets very unhappy when you redefine them

7529, 7534, 7564: Use user-defined drive folder

* Cleanup download message
2018-08-03 05:39:00 -04:00
Ross Scroggs
cc86d67c26 Add textcolor and backgroundcolor to add label/update labelsettings (#770)
* Add textcolor and backgroundcolor to add/update label

pylint cleanup in new valult code

* Check that both textcolor and backgroundcolor are specified
2018-08-02 20:51:47 -04:00
Ross Scroggs
3287a18cac Four fixes (#769)
1) Handle errors in gam-install.sh if user asks for unknown version
2) Over the last few days users get created but their org unit doesn't get assigned. If you say `gam ou /Path/To/Ou print users` you get a trap because you get a user with a primaryEmail and no orgUnitPath.
3) Make delete label take labels like all other label commands
4) Add data transfer service abbreviations like drive; this avoids an API call
2018-08-02 19:27:34 -04:00
Ross Scroggs
135ea0f120 Add owneremail option to print courses (#757) 2018-08-02 19:04:56 -04:00
Jay Lee
468928a2e6 Merge branch 'master' of https://github.com/jay0lee/GAM 2018-08-02 13:29:41 -04:00
Jay Lee
57cafe78f8 Initial Vault Export Work. Very rough 2018-08-02 13:29:13 -04:00
Ross Scroggs
8d27ef7a37 Improve get drivefile (#756)
Addresses Issue #755

Add new options:
targetname `<FileName>`
overwrite
showprogress
2018-07-07 10:49:29 -04:00
Jay Lee
5acad994b6 turn on Cloud Storage API 2018-07-06 19:15:15 -04:00
Jay Lee
6ebdf9ba4e no quotes 2018-07-05 16:45:18 -04:00
Jay Lee
f8067f11e1 Use WIX 3.11.1 2018-07-05 16:43:37 -04:00
Jay Lee
4a0d23d652 What's new 2018-07-05 15:55:39 -04:00
Jay Lee
42be930dfc 4.50, remove redundant collaborative ACL choices 2018-07-05 15:44:58 -04:00
Ross Scroggs
47bc500f40 Make collaborative consistent (#753)
* Make collaborative consistent

* Update documentation

* Allow collaborative abbreviations for all ACL values

* Update documentation

* Update choices per Jay

* Update documentation
2018-07-05 15:39:27 -04:00
Ross Scroggs
d536b6e43a Update documentation (#752) 2018-07-04 20:48:46 -04:00
Jay Lee
5ef91076f9 six.py 1.11.0 2018-07-04 20:45:47 -04:00
Jay Lee
5966e39406 httplib2 0.11.3 2018-07-04 20:24:51 -04:00
Jay Lee
044686b564 pyasn1 0.4.3, pyasn1_modules 0.2.2 2018-07-04 20:14:52 -04:00
Jay Lee
19018e4854 google-auth library 1.5.0 2018-07-04 20:00:39 -04:00
Jay Lee
518e506df4 cachetools 2.1.0 2018-07-04 19:51:43 -04:00
Jay Lee
26ebf30da7 Google API Client 1.7.3 2018-07-04 16:19:51 -04:00
Jay Lee
587fed282d accept simpler values for collaborative, Identity SKU 2018-07-04 15:51:38 -04:00
Ross Scroggs
98cabddcdc Add collaborative to create/update/print group (#750) 2018-07-04 14:07:10 -04:00
Ross Scroggs
419bd01818 Escape ' and \ in drive file parent names (#749) 2018-07-04 13:30:17 -04:00
Ross Scroggs
5a1ab0c168 Clean up/standardize OU id/path handling (#748) 2018-07-04 13:17:51 -04:00
Ross Scroggs
74514b487d Updates (#747)
* Updates

8278/8287, 10374/10394, 11391/11427 - Work around Google list members bug that returns owners/managers as members

10246/10274, 10589/10687 - add query <GroupQuery> to print groups/group-members

* Update documantation

* Update documentation
2018-07-04 13:03:09 -04:00
Ross Scroggs
8e7bd453f4 Updates (#746)
2993, 3625-3648, 12379-12380 - This is my vesion of PR #716

5537-5545 - Code cleanup, only pass callback when making new batxh

8069-8083 - Code cleanup, move big test outside of loop

9269-9270 - Code cleanup, match variable and keyword
2018-07-04 12:35:35 -04:00
Ross Scroggs
348450b9d9 Updates (#745)
2154-2226 - Handle application data transfer keys with no values
2391-2484 - Some you discussed a few months ago, no defaults for teacher and name when createing a course
4051 - allowFileDiscovery can't be changed on an update
2018-07-04 12:14:25 -04:00
Ross Scroggs
a50a481bb5 Updates (#744)
* Three updates

973-1008 - Internally cache discovery documents

1857 - Use patch instead of update so customer address doesn't get wiped out

9917 - In writeCSVFiles, the number of columns is the number of titles. The number of columns in the first row may be smaller than that, think a file with one ACL where other ffiles have many

7171,7173, 10550, 10886, 11655, 12014, 12017 - Standardize error handling

* Fix accidental delete
2018-07-04 12:05:20 -04:00
Ross Scroggs
342e08e8fe Allow multiple queries, more CrOS options (#722)
* Allow multiple queries, more CrOS options

* Minor documentation fixes

* Documentation fixes
2018-07-04 11:04:40 -04:00
Jay Lee
65da6f39dc Update gam-install.sh 2018-05-24 10:41:51 -04:00
Jay Lee
5774b99891 Add G Suite Enterprise for Education SKU 2018-05-18 18:42:08 -04:00
Jay Lee
02ce970aea error reporting on lack of assets value in JSON response 2018-05-17 10:44:07 -04:00
ejochman
f8c24bf86a Add user agent header wrapper to requests handled by AuthorizedHttp (#721)
* Revert patched google_auth_httplib2 and replace functionality by wrapping original library calls

* Wrap calls to google_auth_httplib2.Request__call__ to include a user-agent header.

* Fix bad dict key assignment syntax

* Add user agent header wrapper to requests handled by AuthorizedHttp
2018-04-16 20:34:27 -04:00
ejochman
bbb486f7b2 Stop patching google_auth_httplib2 in favor of wrapping Request.__call__ from gam.py (#718)
* Revert patched google_auth_httplib2 and replace functionality by wrapping original library calls

* Wrap calls to google_auth_httplib2.Request__call__ to include a user-agent header.

* Fix bad dict key assignment syntax
2018-04-16 15:36:07 -04:00
Jay Lee
bdbc7cf713 Merge branch 'master' of https://github.com/jay0lee/GAM 2018-03-29 09:07:25 -04:00
Jay Lee
a44d4f0872 Few fixes for gam alias 2018-03-29 09:07:05 -04:00
Ross Scroggs
16636c34b5 Multiple fixes (#713)
* - Update new create drive options
* - Add gender, keyword, languages attributes to user
2018-03-28 18:01:49 -04:00
Jan Almeroth
ce2e379f30 Add csv option to createDriveFile-command (#707) 2018-03-28 15:15:30 -04:00
Ross Scroggs
4c4f5eef3e Three fixes (#712)
* Two fixes

* Allow empty buildingId when creating/updating a resource calendar with category category_unknown
* Correctly parse `csvfile <FileName>:<FieldName>` on Windows when `<FileName>` contains a drive specificatiom

* Test should be against lowercase value
2018-03-28 15:13:56 -04:00
Jay Lee
6a70a1412b better way to handle per-API batch 2018-03-28 15:12:09 -04:00
Jay Lee
a9025e2aba Use per-API batchPath to work around universal www.googleapis.com/batch URI deprecation 2018-03-26 15:36:14 -04:00
Jay Lee
b1e26e3a48 googleapiclient 1.6.5+ 2018-03-26 15:21:08 -04:00
Ross Scroggs
7fc88f2641 Standardize date/time handling, all can now be relative (#705)
Validate color name/hex pattern
2018-03-21 15:40:25 -04:00
Ross Scroggs
68388a6011 Add fulldatarequired to gam report user/customer (#704)
* Add fulldatarequired to gam report user/customer

Fix missing email address normalization

* Cleanup, fullDataType is always same type, empty means all
2018-03-16 17:54:08 -04:00
Ross Scroggs
481b060caf Course add/delete participant cleanup (#701) 2018-03-15 14:35:47 -04:00
Ross Scroggs
3cb8dae374 Standardize handling delegator/delegate email addresses (#700)
* Standardize handling delegator/delegate email addresses

* Use different atLocs
2018-03-14 12:08:49 -04:00
Ross Scroggs
55999b5b65 Normalize adding domain to user names (#699) 2018-03-13 15:26:41 -04:00
Ross Scroggs
1e126cd633 Standardize getting Boolean values (#695)
* Standardize getting Boolean values

* Fix another OU path query qupting
2018-03-12 10:12:42 -04:00
Ross Scroggs
b6f7ff7038 Error message cleanup; minor coding cleanup (#689)
* Error message cleanup; minor coding cleanup

* A couple more error cleanups

* Trying to replace ` with \` in query, need two \\ to make one \
2018-02-25 15:24:58 -05:00
Thane Gill
8885bdd5f0 python2 as env (#684)
In march 2018 homebrew will point change `python` to python 3:
https://brew.sh/2018/01/19/homebrew-1.5.0/
2018-02-25 15:22:23 -05:00
Jay Lee
35b986e2be Update google libraries 2018-02-09 12:35:13 -05:00
Jay Lee
a80625d5f7 max scopes is now 50, fix crash after success on SA test 2018-01-31 16:57:50 -05:00
Ross Scroggs
d2bb1a83a1 Validate roles, upshift as required by API (#675) 2018-01-19 12:27:30 -05:00
Jay Lee
9f829c6990 Merge branch 'master' of https://github.com/jay0lee/GAM 2018-01-16 14:33:20 -05:00
Jay Lee
6d52e06f66 allow role to be specified with print group-members 2018-01-16 14:33:06 -05:00
ejochman
1c8fcdbe6e Consolidate simple system error exits (#672)
* Consolidate simple system error exits

Use systemErrorExit() to print a consistent string to stderr and exit with the desired return code.

* Consolidate simple system error exits

Use systemErrorExit() to print a consistent string to stderr and exit with the desired return code.

* Fix additional paren
2018-01-15 20:04:59 +00:00
ejochman
b207814ffd Use built-in OAuth2Client Storage delete() instead of manually removing the credential file (#670)
http://oauth2client.readthedocs.io/en/latest/source/oauth2client.client.html#oauth2client.client.Storage.delete
2018-01-04 15:18:53 +00:00
Ross Scroggs
f700e178a1 Standardize get argument loops (#665)
Eliminate numerous if sys.argv[i].lower()
2017-12-22 18:10:07 +00:00
Ross Scroggs
e75c7078ce Move getTimeOrDeltaFromNow to same location as similar routines (#664) 2017-12-22 15:39:00 +00:00
Ross Scroggs
74f7549b7f Fix bug (#663) 2017-12-21 17:36:26 +00:00
Ross Scroggs
d9dc4ac68c Update device files (#661)
* Update device files

info cros/print crosactivity - device files are filtered by dote just line time rnages, recent users

print cros - device files can be printed so user can identify ones to download

* Allow download of all deviceFiles fields
2017-12-21 16:57:10 +00:00
Jay Lee
134f170726 Merge two approaches for GAM header on SA
- Ross approach put headers on both SA token generation request and API calls
- Jay approach appending to existing header if it exists
2017-12-21 10:12:46 -05:00
Ross Scroggs
14fab0293e Set user-agent (#660) 2017-12-21 10:06:29 -05:00
Jay Lee
5318a7a9da GAM User-Agent header for service account requests 2017-12-20 19:01:36 -05:00
Ross Scroggs
46d655b328 The activities API is returning 'netPageToken': '' which causes infinite loop (#659) 2017-12-20 17:09:49 -05:00
Jay Lee
099d0176d6 upgrade google_auth_httplib2 2017-12-20 16:58:03 -05:00
Ross Scroggs
9aaee86120 Favor request (#658)
In my version, gam user foo add calendar xxx ... adds a calendar to foo's list of calendars just like your version

In my version, gam user foo create calendar xxx ... creates calendar xxx for foo

This change reserves a spot for your version to create a calendar and avoids compatibilty issues going forward
2017-12-20 15:49:04 -05:00
Ross Scroggs
e3d3927cfd Prefix buildingId with id: when output, saves time when value is used in gam csv (#657) 2017-12-20 13:59:08 -05:00
Ross Scroggs
c6d0f3da92 Generalize getting building ID (#656) 2017-12-20 13:15:10 -05:00
Jay Lee
156fd4ee6f properly handle cros:device_version_distribution in customer report 2017-12-20 11:45:07 -05:00
Ross Scroggs
f192758f25 Update device file handling (#655)
Add targetfolder option to info cros; it defaults to value of GAMDRIVEDIR

Clean up download device file code

Allow print cros activity to select device files
Default remains activeTimeRanges and recentUsers
both still means activeTimeRanges and recentUsers
all means activeTimeRanges and recentUsers and deviceFiles
2017-12-20 11:09:01 -05:00
Jay Lee
62f3507a32 Delete features 2017-12-20 10:24:13 -05:00
Jay Lee
25800a7883 user location area default to blank, type default to desk 2017-12-20 10:20:37 -05:00
Ross Scroggs
15855bf6bf Final building/feature/resource calendar cleanup (#654)
* Final building/feature/resource calendar cleanup

6905 - buildingId in user does not seem to refer to buildings
8026 - simplify
11284 - 11300 - Allow user to select new resoure calendar fields
11324, 11372 - make callGAPIpages calls similar to all others
11432 - collapse features to a list of names so it canbe fed back to update resource
11438 - Organize output

* Fix typos

* Check user location buildingId, fix typos

* Correct function name
2017-12-20 09:43:00 -05:00
Ross Scroggs
8dd683029b It's not a user error in the API gives us a bogus buildingId (#653)
* It's not a user error in the API gives us a bogus buildingId

* Update documentation, and _getBuildingNameById

You can do what ever you want except exit
2017-12-20 05:05:53 -05:00
Ross Scroggs
d10302ad9d Additional building ID name optimization (#652) 2017-12-19 17:19:21 -05:00
Ross Scroggs
06e74cf44f Convert list to dictionary in _getBuildingNameById (#651)
Speeds up subsequent access by not having to step through list each time
2017-12-19 15:43:31 -05:00
Ross Scroggs
1b46b4b13b New buildings cleanup (#650) 2017-12-19 15:10:22 -05:00
Jay Lee
39d8c93444 start prep for 4.40 2017-12-19 14:12:22 -05:00
Jay Lee
6629f5578c Accept name or ID for building commands 2017-12-19 10:15:00 -05:00
Jay Lee
5d4502e971 More work on print buildings and features, callGAPIpages changed to work w/ non-paged API endpoints 2017-12-16 15:55:07 -05:00
Jay Lee
d070372117 NOT VULNERABLE > NOT IMPACTED for easier scanning 2017-12-16 06:31:30 -05:00
Jay Lee
330871cfbf Buildings and Features and Resources, oh my! 2017-12-16 06:15:16 -05:00
Jay Lee
d6dce1f0fe Include Chrome device TPM info 2017-12-15 12:20:15 -05:00
Ross Scroggs
947c816591 Document devicefiles (#641) 2017-12-14 13:47:08 -08:00
Ross Scroggs
c525d893d3 In gam transfer drive, handle unlimited space in target; clean up info cros downloadfile (#640)
* In gam transfer drive, handle unlimited space in target

* Handle user asking for download files when there aren't any

* Reset downloadurl for each CrOS device

* Once a file is foud to download, break out of the loop
2017-12-14 13:17:02 -08:00
Jay Lee
9ebdfc96a4 Merge branch 'master' of https://github.com/jay0lee/GAM 2017-12-13 17:27:33 -05:00
Jay Lee
1ed0895803 Chrome device log download 2017-12-13 17:26:53 -05:00
Ross Scroggs
eb4b8479f3 Minor cleanups (#636)
* Minor cleanups

In print users allow actual field names isenforcedin2sv and isenrolledin2sv

In print users, add delimiter and sortheaders options

In print groups, add sortheaders option

* Fixed bug in gam oauth create where entering `e` to exit without changes didn't exit.
2017-12-06 19:09:21 -05:00
Ross Scroggs
48fa6b755e Various cleanups (#635)
* Various small cleanups

* Update GamCommands.txt
2017-12-03 14:27:53 -05:00
ejochman
4cef7c4c2d Centralize credential operations and improve separation of concerns (#633)
Improve code reuse and separation of concerns around credential handling by removing duplicate code handling invalid and expired credentials.
2017-12-01 13:20:54 -05:00
Jay Lee
d072172ff5 optimize webcolor get 2017-11-03 20:10:15 -04:00
Jay Lee
6a90a76fb1 Further Team Drive admin work 2017-11-03 20:07:17 -04:00
Jay Lee
0ca083f5e9 Team Drive Theme support 2017-11-03 14:40:38 -04:00
Jay Lee
f8e7ff86ab asadmin flag to give admins special access to Team Drives 2017-11-03 14:12:44 -04:00
Jay Lee
690832b7d7 Allow revoking all ASPs 2017-10-20 11:20:45 -04:00
Jay Lee
78a42f29b1 Merge branch 'master' of https://github.com/jay0lee/GAM 2017-10-20 11:15:07 -04:00
Ross Scroggs
d8be1dbb86 Clean up Course Id handling (#623) 2017-10-19 19:40:15 -04:00
Ross Scroggs
2995327a15 Clean up printer register (#622)
Remove printer register from documentation
Make non-documented command be:
    `gam printer register`
instead of:
    `gam printer xxx register`
as xxx isn't used (it's PrinterID for other commands)
2017-10-16 20:38:00 -04:00
Ross Scroggs
2cc48a0f25 Google oauth cleanup (#621)
49-51: Keep pylint happy

99-1029, 3328: Handle transition from service account to client for calendar

3194: Prepare for client access switching to Google oauth
2017-10-16 20:03:49 -04:00
Ross Scroggs
511d436947 watchGmail cleanup (#620) 2017-10-16 19:46:00 -04:00
Jay Lee
78a3a5f762 Merge branch 'master' of https://github.com/jay0lee/GAM 2017-10-12 12:11:12 -04:00
Jay Lee
c98473d118 handle SA Drive auth failure on CSV Sheets better 2017-10-11 14:57:38 -04:00
Ross Scroggs
5b2e6591bc Prepare for the future (#618) 2017-10-10 13:28:28 -04:00
Jay Lee
63ca5f8054 add cachetools, handle invalid user 2017-10-09 13:44:29 -04:00
Jay Lee
b1a2eb4de5 update SA refresh error, use client_id, not project_id 2017-10-08 21:45:50 -04:00
Jay Lee
a1ca3523a3 upgrade pyasn1_modules to 0.1.4 2017-10-08 21:25:30 -04:00
Jay Lee
ce5eb39f37 upgrade pyasn1 to 0.3.7 2017-10-08 21:22:35 -04:00
Jay Lee
98438644c5 Move service accounts to google-auth, part of #505 2017-10-08 21:14:33 -04:00
Ross Scroggs
18d98a6384 Handle true duplicate showing current role (#616) 2017-10-08 19:43:28 -04:00
Ross Scroggs
ea49c9ef15 Increase todrive max cell count (#615)
* Increase todrive max cell count

* Toss backup file
2017-10-08 14:18:18 -04:00
Jay Lee
ddebd0c974 googleapiclient 1.6.4 2017-10-07 19:37:24 -04:00
Ross Scroggs
6e9c1dc08e Validate states in print courses/course-participants (#613)
This is similar to validation in add/update course

In doPrintCourseParticipants, set fields to minimize data download when gettting course list

Sort titles in print course-participants
2017-10-06 10:09:33 -04:00
Ross Scroggs
7b433940bf Update documentationm (#612) 2017-10-05 16:44:34 -04:00
Jay Lee
dd2fffcfd5 GAM 4.32 2017-10-05 14:40:47 -04:00
Jay Lee
679b5f144d limit course list by courseStates 2017-10-05 10:36:28 -04:00
Jay Lee
f45601435c Handle duplicate error on pending membership status 2017-10-05 10:23:18 -04:00
Ross Scroggs
301cf2f1ba Update error handling in update group (#609) 2017-10-05 09:59:23 -04:00
Ross Scroggs
ba9a3a7980 Update documentation (#607) 2017-10-01 15:22:49 -04:00
Jay Lee
80a002dadc fix datatransfer create error that limited it to 1 parameter 2017-09-27 21:15:20 -04:00
Ross Scroggs
206ba319af Document new transfer service (#604) 2017-09-27 20:30:08 -04:00
Jay Lee
bdb217bed7 Merge branch 'master' of https://github.com/jay0lee/GAM 2017-09-27 19:13:46 -04:00
Jay Lee
0805725a6f Calendar Data Transfer support 2017-09-27 19:13:35 -04:00
Ross Scroggs
df177ac43f When displaying license info to user, show Id and displayName if available (#603)
Plus documentation update thatgot left behind
2017-09-27 18:52:36 -04:00
Ross Scroggs
4ff76bab5c Remove code now supplied by pyinstaller (#602) 2017-09-27 07:04:37 -04:00
Jay Lee
86c4c552db What's new in 4.31 2017-09-26 14:03:04 -04:00
Jay Lee
bc9582bcc1 GAM 4.31 2017-09-20 12:12:13 -04:00
Jay Lee
f383c45e6b passlib 1.7.1 2017-09-20 12:11:31 -04:00
Jay Lee
3bf5ffeb99 simplify no update check for customer update 2017-09-19 18:52:30 -04:00
Ross Scroggs
875879caed Misc updates (#594)
* Misc updates

doUpdateCustomer - skip API call if user didn't update anything

getPhoto - handle uknkown user and user with no photo

doLabel, updateLabels - cleanup, make consistent

doGetGroupInfo - set settings in all cases to avoid having to use try/except

doPrintGroupMembers - message_attributes not used

* Give error when no arguments
2017-09-19 18:50:53 -04:00
Jay Lee
5f28bc82e0 Default value for location area 2017-09-19 17:41:19 -04:00
Ross Scroggs
0327f5c30f Update user attribute handling (#593)
* Update user attribute handling

User attribite type handling issues.

Custom types have their case lowered.

* Use custom keyword as part of type
Old - address, im, phone: type a|b|c|(custom <String>)
New - address, im, phone: type a|b|c|<String>

Dropping custom keyword will break existing scripts

* Use separate keyword for custom type
Old - organization: (type a|b|c) | (customtype <String>)
New - organization: (type a|b|c|<String>)

Dropping customtype keyword will break existing scripts

Unlike all other attributes, you do not set type: custom when customType is set

* Use implied custom
Old - location: type a|b|c|<String>
New - location: type a|b|c|<String>

No change

* Use implied custom, no type keyword
Old - externalid, otheremail, relation, website: a|b|c|<String>
New - externalid, otheremail, relation, website: a|b|c|<String>

No change

This update normalizes the type handling.

Case of custom types is preserved.

In update, for all fields, you can do:
a|b|c|<String>
a|b|c|custom <String>

customtype <String> is still allowed for organization for backward compatability.

* Just in case user does type work and then customtype xxx

* Update documentation
2017-09-19 17:35:52 -04:00
Ross Scroggs
774c084708 Update update group (#592)
* Update update group

Allow plurals of roles, otherwise bad things happen; for example, in the following members isn't taken as the role, it's taken as the list of members (file file.txt is ignored) and the group is trashed.
gam update group group@domain.com sync members file file.txt

In sync, handle googlemail.com as a synonym for gmail.com and dots in googlemail.com/gmail.com addresses. For both the incoming list of members and the current members, the addresses are normalized and then the adds/remove are determined. The actual address is used in the add/remove.

* _cleanConsumerAddress it is
2017-09-19 12:28:35 -04:00
Ross Scroggs
325a06162e Simpler clean up subscriptions (#590)
* Simpler clean up subscriptions

* Include aliases for fields

* Keep pylint happy
2017-09-19 11:58:08 -04:00
Jay Lee
e3dbf56ef5 undo commnet out of IndexError 2017-09-18 14:26:38 -04:00
Ross Scroggs
919f54d0d2 Set universal newline mode for CSV files (#588)
This fixes issue with Mac Excel which uses \r as line separator
2017-09-18 14:25:23 -04:00
Ross Scroggs
4563441a3a Allow user more choices when printing aliases (#587) 2017-09-18 14:09:36 -04:00
Ross Scroggs
323c7da201 Handle case where several user attribute items are set primary (#586)
* Handle where several user attributes items are set primary

* Throw error when multiple items are marked primary
2017-09-16 20:56:00 -04:00
Ross Scroggs
d667da4851 Allow user to request notifications when adding printer ACL (#585) 2017-09-16 20:05:44 -04:00
Ross Scroggs
6be6b2ebde Handle ServerNotFoundError in callGAPI (#584) 2017-09-16 19:48:17 -04:00
Ross Scroggs
8f03a3aabb Handle None return fro call GAPI (#583)
I now can't remember the situation where callGAPI returned None, but Pyton throwing a trap didn't help
2017-09-16 19:43:58 -04:00
Ross Scroggs
5f1489438c Handle situations where version info can't be gotten/is bogus from GitHub (#582)
Terminate with message when forceCheck is true (gam version check)
Otherwise silently proceed
2017-09-16 19:38:04 -04:00
Ross Scroggs
695843d7ec Add csvfile to usergroup_types so gam csvfile filename:heading ... works (#581)
getUsersToModify already handles csvfile, we just need to recognize keyword immediately after gam
2017-09-16 19:27:22 -04:00
Ross Scroggs
b9e4787b1c Show file title/type in createDriveFile (#580)
Initialize variable to prevent trap when giving error message
2017-09-16 18:24:08 -04:00
Ross Scroggs
f2e0b436c6 Map true_values/false_values to True/False (#579)
API doesn't recognize all values in true_values/false_values
2017-09-16 18:11:45 -04:00
Ross Scroggs
e78dbeb056 Use moveDEvicesToOu in doUpdateOrg (#578) 2017-09-16 16:48:36 -04:00
Ross Scroggs
cdbf740d38 Update cancel/delete guardina invitations (#577)
In particular, handle guardian email address in delete; we have to look up invitations with the email address to get the invitation IDs
2017-09-16 16:41:25 -04:00
Ross Scroggs
bbbf7c5391 Update doUpdateCros (#576)
* Update doUpdateCros

Only use moveDevicesToOu if the only field being updated is orgUnitPath; otherwise orgUnitPath will be updated in the loop with whatever other fields are being updated

* Only show count of devices in error message

* Always user moveDevicesToOu to update CrOS OU
2017-09-16 12:49:51 -04:00
Ross Scroggs
ad535b2e3f Correct update group (#575)
Fix typos in var.py
2017-09-16 11:51:56 -04:00
Ross Scroggs
4f30ed6537 For add/update drivefile, allow parent folder not owned by user (#574) 2017-09-16 11:05:22 -04:00
Jay Lee
c6567d7830 commit var.py user types (oops) 2017-09-15 22:22:03 -04:00
Ross Scroggs
a72ef287e3 Minor locations/sshPublicKeys cleanup (#571)
* Minor locations/sshPublicKeys cleanup

Make options names internally consistent and consistent with other commands.

Drop publicsshkeys, too may choices

Change title to SSH Public Keys to match Google's title

* Simplify UserAttributes
2017-09-15 22:20:52 -04:00
Jay Lee
86297a08bd simplify user argument types 2017-09-14 11:41:21 -04:00
Jay Lee
0a81e51072 use parallel API calls instead of batch for membership 2017-09-14 10:24:29 -04:00
Jay Lee
d33eb3b455 googleapiclient 1.6.3 2017-09-13 20:50:08 -04:00
Jay Lee
c6b1a163af throw error (that's caught) if delegated admin can't list parent OUs 2017-09-13 16:08:33 -04:00
Jay Lee
8cca8f642c fix backup code count 2017-09-13 15:42:19 -04:00
Jay Lee
e8f49b8ecc Add posix account attributes for users 2017-09-13 13:07:49 -04:00
Jay Lee
4f1680810b add user public ssh key attributes 2017-09-13 12:51:37 -04:00
Jay Lee
7710578a3c allow location clear 2017-09-13 12:35:50 -04:00
Jay Lee
02aae0d351 Support use location attributes 2017-09-13 11:39:41 -04:00
Ross Scroggs
16512f3507 Support orgUnitPath query in info cros (#567) 2017-09-11 14:37:53 -04:00
Ross Scroggs
91976b2a2b Get all calendar ACLs (#566) 2017-09-11 13:38:42 -04:00
Ross Scroggs
fc44143587 Get course attributes in subroutine (#565) 2017-09-10 20:21:59 -04:00
Jay Lee
70b160373d accept teacher or owner for course owner 2017-09-10 19:47:25 -04:00
Ross Scroggs
bafc648c9d Update vault (#564) 2017-09-10 19:40:25 -04:00
Daniel
e8e9e599f8 add option to specify a time delta (#563) 2017-09-10 14:26:40 -04:00
Jay Lee
9051635bf5 update course owner, show course owner email 2017-09-10 14:22:14 -04:00
Jay Lee
dfb582ecc5 show count of backup codes 2017-09-10 13:50:48 -04:00
Jay Lee
6ea1d6a237 add Vault API 2017-08-14 06:20:49 -04:00
Jay Lee
a758a235dc whatsnew.txt for 4.30 2017-08-13 14:11:38 -04:00
Jay Lee
5c20087f19 oauth2client 4.1.2 2017-08-13 14:03:06 -04:00
Jay Lee
9b942ba05a re-alphabetize scope list 2017-08-13 14:02:22 -04:00
Jay Lee
96b6c89da9 GAM 4.30, report oauth2client version in gam version 2017-08-13 13:43:32 -04:00
Jay Lee
3585d15353 Turn pubsub scope off by default 2017-08-13 13:34:45 -04:00
Jay Lee
d89d99da78 Experimental support for watching user Gmail (pubsub) 2017-08-13 13:32:29 -04:00
Jay Lee
8a7468ef67 Bulk move CrOS devices between OUs 2017-08-13 13:27:03 -04:00
Jay Lee
a76d4a79d1 re-add contacts API 2017-08-08 09:24:45 -04:00
Jay Lee
882e75b7a3 remove contacts.googleapis.com
API no longer available for some reason.
2017-08-07 19:54:06 -04:00
Ross Scroggs
222e00619e Clean up Vault (#542)
* Clean up Vault

* Clean up print vault titles

* More cleanup

* Rework validateCollaborators

* Recode per Jay's request

* Recode per Jay's request
2017-07-28 18:57:57 -04:00
Ross Scroggs
1f7edc5bb9 Additional documentation cleanup/Add missing newline (#541)
* Additional documentation cleanup

* Add missing newline
2017-07-28 07:57:45 -04:00
Ross Scroggs
6a4712ec37 Add Vault commands (#540) 2017-07-27 16:32:47 -04:00
Ross Scroggs
96376669ef Standardize callGAPIpages calls; cleanup new Vault code (#539)
* Standardize callGAPIpages calls; cleanup new Vault code

All calls to callGAPIpages include the items parameter; none of them omit it and take the default value u'items'; thus items can be a positional parameter and the items= in each of the calls can be omitted. Previously, some calls had item= and others didn't.

Cleaned up Vault argument processing.

Clean up some pylint complaints.

* Correct convertUserUIDtoEmail calls in doGetVaultxxxInfo
2017-07-27 10:36:08 -04:00
Ross Scroggs
a94cdbc633 In gam create/update user, allow filed names that match print users column names (#537) 2017-07-27 09:49:18 -04:00
Jay Lee
3cd25f3c10 Update project-apis.txt 2017-07-25 14:43:52 -04:00
Jay Lee
7281a813e0 enable Vault API 2017-07-25 11:04:58 -04:00
Jay Lee
3c1f141339 Vault M&H API Initial Commit 2017-07-25 10:32:53 -04:00
Ross Scroggs
aa51d5be1d Upgrade gam update group to use API batch processing (#530)
* Allow listlimit -1 in print mobile devices

appslimit = -1 and listlimit -1: show no repeating elements
appslimit = 0 and listlimit 0: show all repeating elements
appslimit = N and listlimit N: show N repeating elements

* Upgrade gam update group to use API batch processing

This allows gam update group sync commands to be used in gam batch and gam csv commands.

Add/delete/update and clear will be faster due to batching.

GAPI exception handling improved to support additional error checking.
2017-07-25 09:41:42 -04:00
Ross Scroggs
bb60488bf3 gotTopLevelOrg must take orgUnitPath as parameter (#527)
THis is required in doPrintOrgs when fromparent is used.
2017-07-07 13:16:42 -04:00
Ross Scroggs
4fbabd9f35 Clean up create/update project (#526)
Make function eo t=enable project APIs
2017-07-07 11:37:39 -04:00
Jay Lee
f8750fe0b6 Don't send state for new courses (defaults to provisioned) 2017-07-05 17:04:06 -04:00
Ross Scroggs
420fb1c393 Make function to get course state; cleanup (#525) 2017-07-01 14:58:17 -04:00
Ross Scroggs
7628b5a08d Update print mobile (#524) 2017-07-01 12:30:25 -04:00
Jay Lee
9038587f67 update course state also 2017-07-01 09:59:38 -04:00
Jay Lee
824dad6fab pull course states dynamically from discovery 2017-07-01 09:38:52 -04:00
Jay Lee
dbd2960841 Create courses in active state by default 2017-06-30 22:28:16 -04:00
Ross Scroggs
b02fa6c358 Make function to get top level Org Id (#522) 2017-06-30 22:20:16 -04:00
Ross Scroggs
bc1c51894c Pacify pylint, fix error messages (#521) 2017-06-30 21:25:07 -04:00
Ross Scroggs
6136ece5dd Code fixes (#520)
* Delete debugging print statements

* Add missing \n to message
2017-06-30 20:00:52 -04:00
Jay Lee
bb204fa868 sort pring org results 2017-06-30 16:08:28 -04:00
Jay Lee
ce84ad8774 code cleanup 2017-06-30 15:08:56 -04:00
Ross Scroggs
597a236e05 Directory API Mobiledevices update no longer supported (#519) 2017-06-30 15:03:50 -04:00
Ross Scroggs
180606f721 Updated gam create group to avoid a Google API issue that generated an error when the group description contains a <, > or =. (#518) 2017-06-30 11:58:14 -04:00
Jay Lee
dcfa718a9b Always use update for directory API. patch is slower and problematic. API console shows it does get then update. 2017-06-30 11:39:10 -04:00
Jay Lee
6858f87926 fix gam print mobile 2017-06-30 10:17:58 -04:00
Jay Lee
743e808404 fixes/improvements for uid conversions as well as org get/list 2017-06-30 10:04:40 -04:00
Jay Lee
0ffb257f08 Merge branch 'master' of https://github.com/jay0lee/GAM 2017-06-29 16:23:07 -04:00
Jay Lee
d670d5feab 'gam project update' to enable new APIs on existing project 2017-06-29 16:22:57 -04:00
Jay Lee
f34859f51b Update project-apis.txt 2017-06-29 16:00:35 -04:00
Jay Lee
42ae43e81e GAM 4.23 2017-06-24 13:38:14 -04:00
Ross Scroggs
41cad79a21 Update documentation (#513) 2017-06-21 15:39:47 -04:00
Jay Lee
92c7525d0a Merge branch 'master' of https://github.com/jay0lee/GAM 2017-06-21 14:37:31 -04:00
Jay Lee
86e496040c fix elif 2017-06-21 14:37:20 -04:00
Ross Scroggs
21c2ecfd1d Make code in orgUnitPathQuery more readable (#512) 2017-06-21 14:20:44 -04:00
Jay Lee
720bd46683 Filter Chrome devices by OU 2017-06-19 15:00:11 -04:00
Jay Lee
b83967809d oauth2client update 2017-06-19 14:49:28 -04:00
Ross Scroggs
385d4e8ab2 Update documentation (#504) 2017-05-29 11:56:21 -04:00
Jay Lee
96d52f47d1 Catch and report if no usage reports avail (new domain). 2017-05-29 11:55:07 -04:00
Jay Lee
e4353189dc Merge branch 'master' of https://github.com/jay0lee/GAM 2017-05-24 20:42:02 -04:00
Jay Lee
dbe8dc67f2 Add G Suite Free/Standard SKU 2017-05-24 20:41:28 -04:00
Ross Scroggs
4998c30d20 Update create/update group, use update semantics (#501) 2017-05-22 19:43:58 -04:00
Ross Scroggs
20e84b9c9a On create group, use Group Settings to set description with new lines (#500) 2017-05-20 17:33:57 -04:00
Jay Lee
efdaa6a64e Update README.md 2017-05-20 16:14:59 -04:00
Ross Scroggs
1a96622366 Update calendar to allow access to user's secondary calendars (#499)
* Update calendar to allow access to user's secondary calendars

* Code cleanup

* Code cleanup

* Code cleanup
2017-05-20 15:34:25 -04:00
Jay Lee
d5af189125 Report Basic/Business/Enterprise user and license counts in gam info domain
Note that there is currently an error on Google's side where license count
sometimes incorrectly reports user count instead of total licenses shown
in the admin console, Google is aware of the issue and should be fixing at
some point.
2017-05-20 12:47:13 -04:00
Ross Scroggs
5ebaf8264a Alphabetize scopes for easier reading (#498) 2017-05-20 12:33:02 -04:00
Ross Scroggs
40bfde9697 Fix indentation in doCreateProject; fix typo in gam-install.sh (#496) 2017-05-15 14:14:22 -04:00
Jay Lee
a52805b35e Fix Cloud Org ACLs when possible 2017-05-13 15:23:53 -04:00
Jay Lee
0312258db7 Check if token exists before delete so GAM output is accurate 2017-05-08 12:46:24 -04:00
Ross Scroggs
2ddd9e2477 Fix gam csv/Unicode issues on Windows (#492) 2017-04-26 16:10:57 -04:00
Ross Scroggs
be44d2c601 Implement _getValueFromOauth (#491) 2017-04-26 13:34:52 -04:00
Jay Lee
ee44ba67cf Update gam-install.sh 2017-04-24 13:00:16 -04:00
Jay Lee
c462216be7 Update gam-install.sh 2017-04-24 12:45:06 -04:00
Jay Lee
e43ebd4d40 Update gam-install.sh 2017-04-24 12:38:35 -04:00
Jay Lee
0ea0c416ec Default to 0 (no limit) print jobs 2017-04-21 13:36:24 -04:00
Ross Scroggs
3f42eb86d2 Fix typo (#482) 2017-04-17 18:54:14 -04:00
Jay Lee
b003e5065a Merge branch 'master' of https://github.com/jay0lee/GAM 2017-04-17 10:42:19 -04:00
Jay Lee
cc25c40406 strip whitespace from beginning and end of client id/secret 2017-04-17 10:42:11 -04:00
Ross Scroggs
8e3c632a7c Handle new OAuth2 Token error, ignore periods in messages (#481)
* Handle new OAuth2 Token error, ignore periods in messages

* Fix typo
2017-04-17 10:35:03 -04:00
Ross Scroggs
ab94208bb5 Use update instead of patch for update user (#480)
This is required in order for update user
phones/orgamizations/addresses/... clear to work
2017-04-16 15:14:17 -04:00
Jay Lee
0562ed3eb9 GAM 4.22 2017-04-16 13:21:38 -04:00
Ross Scroggs
3ceef052a3 Optimize print job downloads (#476) 2017-04-16 13:11:36 -04:00
Jay Lee
d5b5251587 error out instead of creating new projects with unused file names. 2017-04-16 13:10:35 -04:00
Jay Lee
0ae73fa6b3 Implement checks for valid client id and secret 2017-04-07 21:37:43 -04:00
Ross Scroggs
38b6fd213f Add countsonly option to gam print courses to get student/teacher counts without full profiles (#473)
* Add countsonly option to gam print courses

* When countsonly is true, only get ids, not full profile

* Update documentation
2017-04-03 15:21:00 -04:00
Ross Scroggs
39193ae92f Add debugging to print printjobs, add additional termination check (#472) 2017-04-03 13:31:58 -04:00
Jay Lee
c8c18497cc cleanup wixpdb files 2017-04-01 13:14:05 -04:00
Jay Lee
bdb0b3d6dc GAM 4.21 2017-04-01 12:53:11 -04:00
Ross Scroggs
7312c8f396 Standardize info cros/print cros/print crosactivity (#470) 2017-04-01 12:05:15 -04:00
Jay Lee
831272a137 Revert "Standardize info cros/print cros/print crosactivity (#468)" (#469)
This reverts commit d50231b888.
2017-04-01 10:30:29 -04:00
Ross Scroggs
d50231b888 Standardize info cros/print cros/print crosactivity (#468)
* Standardize info cros/print cros/print crosactivity

* Move _filterTimeRanges before 1st function that uses it.
2017-04-01 10:25:20 -04:00
Ross Scroggs
6160bb0953 Fix calendar add ACL (#465) 2017-03-31 13:27:49 -04:00
Ross Scroggs
1e013e6cd7 Fix add smime, isDefault can not be set on insert (#458)
* Update print drive settings to reflect change to Drive v3

* removeExpiration not passed to API

* Update documentation

* organizer is valid role on update drivefileacl

* Fix add smime, isDefault can not be set on insert
2017-03-31 13:02:34 -04:00
Jay Lee
96955d9305 Merge branch 'master' of https://github.com/jay0lee/GAM 2017-03-27 11:39:49 -04:00
Jay Lee
fdfa38a209 gam print crosactivity 2017-03-27 11:39:26 -04:00
Ross Scroggs
daa4b57af1 Drive v3 related changes (#456)
* Update print drive settings to reflect change to Drive v3

* removeExpiration not passed to API

* Update documentation

* organizer is valid role on update drivefileacl
2017-03-24 15:25:24 -04:00
Ross Scroggs
de3be6ba52 Update documentation (#454)
* Fix create project to handle Google change

* Handle newlines in app name

Don’t output name/value for
name == u'accounts:authorized_apps’, apps are listed later

* Cleanup

My typos: 614, 813
Pylint: 887, 3618/4184, 8657, 8751
Your typo: 6794

* Update documentation
2017-03-22 22:05:57 -04:00
Ross Scroggs
81c2e425ef Clean up (#453)
* Fix create project to handle Google change

* Handle newlines in app name

Don’t output name/value for
name == u'accounts:authorized_apps’, apps are listed later

* Cleanup

My typos: 614, 813
Pylint: 887, 3618/4184, 8657, 8751
Your typo: 6794
2017-03-22 20:39:44 -04:00
Jay Lee
4bf6b6fb96 What's new in 4.2 2017-03-22 16:56:37 -04:00
Ross Scroggs
4ed8497bd7 Minor fixes to showReport (#452)
* Fix create project to handle Google change

* Handle newlines in app name

Don’t output name/value for
name == u'accounts:authorized_apps’, apps are listed later
2017-03-22 15:45:20 -04:00
Ross Scroggs
738280bbe5 Fix create project to handle Google change (#451) 2017-03-22 14:33:27 -04:00
Jay Lee
ad9384aeac Soft errors on team drive functions 2017-03-22 13:59:38 -04:00
Jay Lee
67f5416858 Team Drive CSV and screen output 2017-03-22 13:34:28 -04:00
Jay Lee
51567ff5c4 fix report users, include msgValue items in report customer 2017-03-22 12:52:10 -04:00
Ross Scroggs
6f6a94c9b0 Handle bad data from Google in gam report customer (#450) 2017-03-22 11:12:22 -04:00
Jay Lee
08bd3ecc91 Get 4.2 ready 2017-03-19 14:06:17 -04:00
Jay Lee
6ebc0f4e81 Moar v3, Moar Team Drive 2017-03-19 14:05:46 -04:00
Jay Lee
bf39798263 Update Drive ACLs to v3 2017-03-18 21:07:51 -04:00
Jay Lee
92521acfa3 More v3 work 2017-03-18 20:36:45 -04:00
Jay Lee
f8d43a19c1 User counts for gam info domain 2017-03-18 13:32:48 -04:00
Jay Lee
842ddc2a26 httplib 0.10.3, api-client 1.6.2 2017-03-18 12:17:19 -04:00
Jay Lee
bafed078e5 Fix report name for todrive 2017-03-17 22:07:41 -04:00
Jay Lee
8999fb84de Switch report uploads to use SA, remove Drive scope 2017-03-17 20:05:06 -04:00
Jay Lee
3b162924c5 back to email scope, use SA for all Gmail API calls 2017-03-17 19:26:16 -04:00
Jay Lee
242c61205d Switch Calendar to use SA only 2017-03-17 12:57:31 -04:00
Jay Lee
139727dd33 Use service accounts for Calendar ACLs 2017-03-17 12:39:17 -04:00
Ross Scroggs
a52341e29e Improve API caching options, cache errors (#446)
* Improve API caching options, cache errors

* Clarify argument names

* All or nothing caching

I changed the argument names and implemented your proposal:
	nocache.txt: ignored
	allcache.txt present: all caching
        default: no caching
2017-03-16 11:23:42 -04:00
Jay Lee
c2358f60fb More Team Drive Work 2017-03-16 06:23:12 -04:00
Jay Lee
c8ea108be3 Use https://git.io/gam 2017-03-15 19:40:17 -04:00
Jay Lee
8fdd0abc53 scopes for drive3 2017-03-15 16:46:52 -04:00
Jay Lee
f396b2f476 Create Team Drives, show permissions and info 2017-03-15 16:07:40 -04:00
Jay Lee
6e9413eada drive3 API for Drive v3 API 2017-03-15 15:04:46 -04:00
Ross Scroggs
30a5467b82 Optimize License handling (#440)
* Handle batch file errors, clean up commit-batch messages

* Optimize License handling
2017-03-15 14:57:56 -04:00
Jay Lee
3ffa3ca5e5 If we can't get a safe filename, name the file after it's ID 2017-02-21 15:03:45 -05:00
Ross Scroggs
f0351b8bec Handle batch file errors, clean up commit-batch messages (#439) 2017-02-21 13:57:55 -05:00
Jay Lee
d8093fa1f0 Merge branch 'master' of https://github.com/jay0lee/GAM 2017-02-19 15:54:50 -05:00
Jay Lee
2080f33fdb What's new in 4.12 2017-02-19 15:54:27 -05:00
Ross Scroggs
6a2925c546 pool.join required to wait for processes to finish (#436) 2017-02-19 10:53:42 -05:00
Jay Lee
26960c96d9 Improve batch to handle CTRL+C and print status 2017-02-19 08:49:31 -05:00
Jay Lee
58a080fe6b 4.12, delete message fix 2017-02-18 17:20:49 -05:00
Ross Scroggs
3c3b7527a1 Clean up SKU handling (#432) 2017-02-18 16:57:42 -05:00
Jay Lee
0d7028416a Reduce key size 2017-02-13 10:26:25 -05:00
Ross Scroggs
aa6dca4b4c Keep the pylint wolf at bay (#430)
* Fix bug, update ducumantation

* Clean up error messages

* Exit on error, fix bug

* One more bug fix

* Update documentation, fix code

l_sku can never match a_sku.lower() because it has -'s stripped and
a_sku doesn't

* Keep the pylint wolf at bay

* Clean up code, avoid try/except
2017-02-11 08:31:34 -05:00
Ross Scroggs
3d7a7bd609 Update documentation, fix code (#429)
l_sku can never match a_sku.lower() because it has -'s stripped and
a_sku doesn't
2017-02-10 22:00:32 -05:00
Jay Lee
35854af1e9 Improve handling and display of licenses
- displayName added so what admin sees better matches admin console
 - better aliases for each skuId
 - admin can specify exact productId they wanted (would have solved renaming of Enterprise SKU issue)
2017-02-09 09:35:54 -05:00
Jay Lee
4450652c32 Google Changed SKU for Enterprise license :-( 2017-02-08 14:44:02 -05:00
Ross Scroggs
d34e09f8d5 One more bug fix (#426)
* Fix bug, update ducumantation

* Clean up error messages

* Exit on error, fix bug

* One more bug fix
2017-02-07 12:33:25 -05:00
Ross Scroggs
610ba5bf6a Exit on error, fix bug (#425)
* Fix bug, update ducumantation

* Clean up error messages

* Exit on error, fix bug
2017-02-07 11:53:59 -05:00
Ross Scroggs
65debc6a27 Clean up error messages (#424)
* Fix bug, update ducumantation

* Clean up error messages
2017-02-07 11:18:31 -05:00
Ross Scroggs
61868d5fde Fix bug, update ducumantation (#423) 2017-02-07 11:01:59 -05:00
Ross Scroggs
998f4c6dff Make reseller commands consistent (#422) 2017-02-07 10:44:26 -05:00
Ross Scroggs
a2a8393775 Fix bugs (#421) 2017-02-07 09:43:51 -05:00
Jay Lee
d9ec9d3af9 Merge branch 'master' of https://github.com/jay0lee/GAM 2017-02-04 14:59:07 -05:00
Jay Lee
7104b24633 more work on Reseller API 2017-02-04 14:57:42 -05:00
Ross Scroggs
9cd81c9148 Use different index for inner loop (#419) 2017-02-03 19:42:59 -05:00
Ross Scroggs
56a60d1651 Fix add/update/delete smime when sendas argument not specified (#418)
* Simplify by using existing function

* add/update smime using 1st users email address when sendas not specified

* Fix error messages, update documentation

* Delete smile needs same fix

* Delete/update have same problem with smimeId

* Clean up names

* Missed one
2017-02-03 18:27:55 -05:00
Ross Scroggs
1cdf02bc31 Simplify by using existing function (#417) 2017-02-03 15:50:34 -05:00
Jay Lee
5b469496d6 Initial S/MIME support (requires Enterprise SKU) 2017-02-03 13:29:36 -05:00
Ross Scroggs
2a1d3a1ce1 Default encoding has to be set on multiprocess instances of gam (#416)
* Default encoding has to be set on multiprocess instances of gam

* Update documentation
2017-02-03 13:02:31 -05:00
Boris Hajduk
9b89492d83 We can now query 2SV status in the print users command (#415) 2017-02-03 11:35:52 -05:00
Boris Hajduk
45b1eee8f3 early support to get info user info about 2-step verification enrollment (#414) 2017-02-03 11:08:45 -05:00
Jay Lee
e9dda2079a Add reseller API to projects to enable 2017-02-01 10:27:13 -05:00
Ross Scroggs
c279acbab8 Enhance print courses/course-participants (#413) 2017-02-01 09:09:02 -05:00
Jay Lee
082cd9b087 First commit of Reseller API support 2017-01-31 16:06:32 -05:00
Ross Scroggs
7d2e5d674a Handle user error in update labels (#412)
* Get only required fields when processing messages

* Handle customer id in group add/delete/sync/clear

I always wondered what this was: if user_email != u'*'

* Allow * to mean all users in group operations

* Make pylon happy, handle user error in update labels
2017-01-31 15:48:47 -05:00
Ross Scroggs
b2d95c545d Handle customer id in group add/delete/sync/clear
 (#411)
* Get only required fields when processing messages

* Handle customer id in group add/delete/sync/clear

I always wondered what this was: if user_email != u'*'

* Allow * to mean all users in group operations
2017-01-31 14:52:21 -05:00
Ross Scroggs
127c3125ad Get only required fields when processing messages (#410) 2017-01-31 13:27:11 -05:00
Jay Lee
63f1e0d504 Merge branch 'master' of https://github.com/jay0lee/GAM 2017-01-31 12:53:32 -05:00
Jay Lee
1ad10b6954 G Suite Enterprise SKU 2017-01-31 12:53:00 -05:00
Ross Scroggs
820cf98087 Fix typos in new messages/threads command parsing (#407)
* Soft error when getting group members, fix typo

* Fix typos
2017-01-27 14:54:12 -05:00
Jay Lee
15bd6beebd Threads and batch support for Gmail modify/delete 2017-01-27 11:03:25 -05:00
Ross Scroggs
31871f192c gam group <GroupName> should only return users except when updating groups (#406) 2017-01-26 15:18:34 -05:00
Ross Scroggs
7acfa4a2ac Handle newlines in various description fields (#405) 2017-01-26 14:34:46 -05:00
Ross Scroggs
e212d68d60 Move project-apis.txt URL to var.py so all urls are in same spot (#403) 2017-01-26 13:27:22 -05:00
Ross Scroggs
6aeee89ee4 Handle group members of type CUSTOMER (#402) 2017-01-26 13:06:15 -05:00
Ross Scroggs
623572a652 Soft error when getting group members, fix typo (#401) 2017-01-26 11:07:20 -05:00
Jay Lee
f7c587c08b no argument to -l 2017-01-25 15:30:33 -05:00
Jay Lee
da1f346b1a -l argument to bash script for upgrade only 2017-01-25 15:27:12 -05:00
Jay Lee
056b56ed78 GAM 4.11 part 2 2017-01-25 14:31:20 -05:00
Jay Lee
791581b850 GAM 4.11 2017-01-25 14:29:36 -05:00
Ross Scroggs
08f426ba09 Allow suppression of empty entries in multivalued schema fields (#399) 2017-01-25 14:21:58 -05:00
Jay Lee
54371cffff remove .apps.googleusercontent.com from client id for auth 2017-01-25 11:35:33 -05:00
Jay Lee
c7946c0edf remove access_type=offline to shorten auth URLs as it's default already 2017-01-25 11:22:25 -05:00
Jay Lee
ae578c64a0 rename to simplehttp so we don't affect http 2017-01-25 09:02:11 -05:00
Jay Lee
08bc1898cc fix splitlines 2017-01-25 08:57:23 -05:00
Jay Lee
dc626b1b3e always use httplib2 so we are consistent with TLS verify, debug output, etc 2017-01-25 08:53:33 -05:00
Ross Scroggs
7283e06750 Allow newlines in calendar event descriptions. (#398) 2017-01-24 15:36:12 -05:00
Ross Scroggs
c76368509e rint start_time and end_time as dates in gam reports; handle bad data from Google (#397) 2017-01-24 14:51:43 -05:00
Jay Lee
b54eb97f6b Use profile instead of email scope since it's 1 less scope ultimately 2017-01-24 14:50:26 -05:00
Jay Lee
2d997fb046 silence noisy oauth2client helpers 2017-01-24 14:49:37 -05:00
Jay Lee
e2cf769b20 short URLs fix back 2017-01-24 14:48:44 -05:00
Jay Lee
281786b3b9 prettify oauth2.txt 2017-01-24 14:04:56 -05:00
Jay Lee
b06b8608d0 googleapiclient 1.6.1 2017-01-24 14:03:03 -05:00
Jay Lee
22dc39eb85 Merge branch 'master' of https://github.com/jay0lee/GAM 2017-01-24 14:00:32 -05:00
Jay Lee
4a894958f0 oauth2client 4.0 2017-01-24 14:00:07 -05:00
Ross Scroggs
38273a786a Supply missing imports in utils.py (#396) 2017-01-24 13:47:56 -05:00
Jay Lee
6ba0a5d942 4.1 whatsnew.txt 2017-01-24 13:20:05 -05:00
Jay Lee
2ad731f4e0 GAM 4.1 2017-01-24 13:15:16 -05:00
Jay Lee
a491fb5471 Update ToS page for projects 2017-01-24 13:14:45 -05:00
Jay Lee
33cfb940b4 remove accidental break 2017-01-24 12:17:52 -05:00
Jay Lee
46b79334e4 pull APIs from GitHub master, import urllib2 once 2017-01-24 12:15:11 -05:00
Jay Lee
a7e841bcba put APIs to enable for project in own file to pull live 2017-01-24 12:00:17 -05:00
Jay Lee
f2400b35b0 Update API list for create project 2017-01-16 15:29:25 -05:00
Ross Scroggs
31058336ce Fix typo, clean up imports (#381)
* Fix typo, utils.py needs a few globals

* Clean up imports
2017-01-04 20:23:53 -05:00
Ross Scroggs
ac2cbef7f8 Fix gam transfer drive to properly handle orphaned files (#380) 2017-01-04 19:19:49 -05:00
Ross Scroggs
d7187ff998 When default SKU/Product list is used, sort it so output is cleaner (#379) 2017-01-04 17:57:23 -05:00
Ross Scroggs
2cc79f44ea Update licenses (#376)
* Update documentation for new license aliases

Add government as in PR #362

* Allow full SKU to be specified
2016-12-31 11:50:39 -05:00
Jay Lee
067a67c14e Update README.md 2016-12-31 11:32:40 -05:00
Ross Scroggs
054addfa9b Fix typo in documentation (#375) 2016-12-31 10:42:43 -05:00
Jay Lee
6b7cf875de utils.py for simple util functions 2016-12-29 16:32:22 -05:00
Jay Lee
581e31499b move more vars to var.py 2016-12-29 15:44:33 -05:00
Jay Lee
a5883a8429 consolidate license info 2016-12-29 14:31:54 -05:00
Jay Lee
95c2d91a5b pull variables into their own file 2016-12-29 13:55:14 -05:00
Ross Scroggs
9f487d57fb Get just the field we need in info user licenses (#374) 2016-12-27 18:28:21 -05:00
Ross Scroggs
e0d278e7ea Added count and allfields arguments to gam print groups (#373) 2016-12-27 15:30:09 -05:00
Ross Scroggs
36725c3574 Fix typo, update documentation (#372) 2016-12-27 12:44:50 -05:00
Ross Scroggs
8c911215b1 Site Verification API should be included in project APIs (#371) 2016-12-27 12:37:35 -05:00
Ross Scroggs
2b57a976c2 Clean up new batch licensing (#370) 2016-12-27 11:49:37 -05:00
Ross Scroggs
4817ce282a Handle suspended users with calendar commands; improve show calsettings output (#369) 2016-12-27 11:34:45 -05:00
Ross Scroggs
2da6666587 Add directmemberscount to list of group field names (#368) 2016-12-27 11:00:52 -05:00
Jay Lee
07f1bc050a Improve user info response with license batching 2016-12-27 10:57:18 -05:00
Ross Scroggs
9135e15b12 Fix error handling for nonexistent users in data transfers (#367) 2016-12-27 10:31:26 -05:00
Ross Scroggs
5c32a86257 Use patch instead of update in doUpdateCustomer, update is broken (#366) 2016-12-27 10:08:31 -05:00
Ross Scroggs
d5bcac4d14 Fix run-batch to handle 0 items (#363) 2016-12-21 15:04:30 -05:00
Ross Scroggs
73bcadbbfe Handle Google-Apps-For-Government (#362) 2016-12-21 14:05:59 -05:00
Jay Lee
d3091d3dd8 Update README.md 2016-11-20 15:39:46 -05:00
Jay Lee
f9ab78e393 update some strings to G Suite 2016-11-20 14:18:28 -05:00
Jay Lee
7a94077906 Update README.md 2016-11-20 13:58:36 -05:00
Jay Lee
6b438c3a46 Update README.md 2016-11-20 13:57:45 -05:00
Jay Lee
5383ed22ea Update README.md 2016-11-20 13:53:22 -05:00
Ross Scroggs
dbd09daa33 Fix Windows multiprocessing (#346)
* Fix Windows multiprocessing

* Clean up Windows initial setup
2016-11-19 13:23:33 -05:00
Ross Scroggs
25ace13a3d Handle additional run_batch calls (#344) 2016-11-19 07:16:23 -05:00
Ross Scroggs
8fc6112e32 Handle folder alternateLink (#343) 2016-11-18 19:36:54 -05:00
Ross Scroggs
ac2eb99d63 Handle Google mis-reporting invalid group in gam print groups settings (#342) 2016-11-18 18:32:55 -05:00
Ross Scroggs
43707d8074 Create missing label with gam add filter (#340)
Restores behavior present with Email Settings API
2016-11-18 17:33:43 -05:00
Jay Lee
b0b3c18e99 use multiprocessing instead of threading to take advantage of multi CPU systems 2016-11-18 15:45:57 -05:00
Jay Lee
349f2801c5 Improve batch performance
* Use a single GAM / Python process for all threads (needs testing, will sys.exit in a function cause issues?)
    - Huge reduction in useless time spent starting Python per-thread.
  * Bump default from 5 threads to 25.
  * Introduce default_to_batch for some user commands where it makes sense.
    - most show/print commands will default to batch off.
    - most do / update commands will default to on.
2016-11-18 10:19:19 -05:00
Ross Scroggs
658e7beb2b Standardize handling oauth2.txt (#337) 2016-11-17 11:57:10 -05:00
Ross Scroggs
e777eb6c99 Handle Google reporting invalid when getting group settings (#335) 2016-11-16 12:45:04 -05:00
Ross Scroggs
8a6ce43ad3 Do not get settings for special groups abuse and postmaster (#334)
* In gam print groups settings, get gs service outside of loop

* Do not get settings for special groups abuse and postmaster

* Do not set settings for special groups abuse and postmaster
2016-11-15 22:36:59 -05:00
Jay Lee
72d182032b point to create project / check serviceaccount rather than Wiki 2016-11-15 13:25:54 -05:00
Ross Scroggs
9b8189a3e4 In gam print groups settings, get gs service outside of loop (#333) 2016-11-15 09:21:05 -05:00
Jay Lee
14eb9ca3f8 Cleanup signature/vacation processing (#332) 2016-11-14 16:22:58 -05:00
Ross Scroggs
cc2cba8c70 Cleanup signature/vacation processing 2016-11-14 13:16:07 -08:00
Ross Scroggs
17660220fe Added html argument to gam add sendas/update sendas/signature to control newline (NL) processing when signature is read from a file. (#331) 2016-11-13 15:27:15 -05:00
Ross Scroggs
d5538a79da Update NL handling in doSignature and doVacation (#330) 2016-11-13 13:48:32 -05:00
Ross Scroggs
fc5cd1c219 Improve Unicode handling when reading files. (#329) 2016-11-13 13:02:00 -05:00
Ross Scroggs
e10e63a87f Cleanup doDelProjects/doCreateProject (#327)
Move common code to get CRM service into separate routine
Have delete projects get login_hint like oath create and create project
2016-11-12 16:09:17 -05:00
Ross Scroggs
93b05de15e Update build scripts (#326) 2016-11-12 13:07:19 -05:00
Ross Scroggs
a6a94060c6 Standardize getting login_hint for create project and oauth request (#325)
* Use RE to validate login_hint email address

http://stackoverflow.com/questions/201323/using-a-regular-expression-to-
validate-an-email-address

Per the W3C HTML5 spec:

* Use simpler RE pattern for email validation
2016-11-11 11:26:17 -05:00
Jay Lee
a12ec0ffdc Google API Client 1.5.5 2016-11-11 09:34:02 -05:00
Jay Lee
ae5d484309 GAM 4.03 2016-11-11 09:23:01 -05:00
Jay Lee
2cdcc6f5ad gam-install.sh fix for Ubuntu 16+ where python 2 not always present 2016-11-10 20:28:48 -05:00
Jay Lee
3699e0199b mktemp fix to run on OS X 10.10 2016-11-08 22:02:20 -05:00
Ross Scroggs
5ec417d50c Validate full email address on gam oauth create <EmailAddress> (#321)
* Validate full email address on gam oauth create <EmailAddress>

* Update documentation
2016-11-08 16:32:38 -05:00
Ross Scroggs
240edd6812 Simplify doCheckServiceAccount (#320)
* Simplify doCheckServiceAccount

* Further simplify doCheckServiceAccount

List of scopes is all we need

* Build all_scopes list outside of loop, sort once

* Leave use_scopes in buildGAPIServiceObject for future use
2016-11-08 10:33:35 -05:00
Jay Lee
faa1b87926 Merge branch 'master' of https://github.com/jay0lee/GAM 2016-11-07 12:23:09 -05:00
Jay Lee
08a9764465 delete projects (undocumented), catch API ToS errors and try to point admin to correct URL before retrying. 2016-11-07 12:22:54 -05:00
Ross Scroggs
e3a73ce7d1 Process NLs in user structured/formatted addresses (#319) 2016-11-07 10:40:04 -05:00
Ross Scroggs
d75a5e78a5 Have doCreateProject find proper JSON files (#318)
As in https://github.com/jay0lee/GAM/pull/313, factor in the user's
environment variables. On a new installation, the files will be in GAM
Path, but on an existing installation the environment variables may
have them located somewhere else.
2016-11-07 10:21:55 -05:00
Jay Lee
6a179215d6 sendNotifications = None 2016-11-05 20:14:05 -04:00
Ross Scroggs
c2dc2f0712 Fixes/Updates (#317)
GamCommands.txt
Update documentation

gam.py
doCheckServiceAccount
Correct my previous update - Lines 1211/1241

doCalendarDeleteEvent:
Eliminate duplicate events - Line 3337
Fix error message - LInes 3344/2245
Pass sendNotifications to API - LInes 3323,3349

doCalendarAddEvent
Update error message - LIne 3461

doGetCrosInfo
Add query capability - Lines 7992/8078
2016-11-05 20:12:51 -04:00
Jay Lee
b6b6824ee1 whatsnew for 4.02 2016-11-04 14:25:59 -04:00
Jay Lee
2902fc8931 Merge branch 'master' of https://github.com/jay0lee/GAM 2016-11-04 14:14:27 -04:00
Jay Lee
1a6ec398b2 gam calendar <> deleteevent 2016-11-04 14:14:21 -04:00
Ross Scroggs
7d849e0cc0 Code fixes, cleanup (#315)
* Code fixes, cleanup

Fix doCheckServiceAccount to pass correct api to buildGAPIServiceObject
pylint cleanup
Simplify passing login_hint to doRequestOAuth in main
Give error if invalid argument after gam <users> check

* Fix error

* Use try accept
2016-11-04 14:13:39 -04:00
Jay Lee
2958bd9f86 GAM 4.02 2016-11-04 12:02:35 -04:00
Jay Lee
15d93c9e5d pause at end of gam-setup.bat 2016-11-04 11:48:10 -04:00
Jay Lee
082c34b453 prompt for regular user to test service account. 2016-11-04 11:39:20 -04:00
Jay Lee
32e7932050 fix potential IndexError 2016-11-03 14:42:07 -04:00
Jay Lee
6becd08f3c fix login_hint issues 2016-11-03 14:30:14 -04:00
Jay Lee
26fbf9c524 don't run setup on upgrades 2016-11-03 14:20:15 -04:00
Ross Scroggs
d9124f3ffa Fix doRequestOAuth to find proper client secrets JSON file (#313)
The environment variables GAMUSERCONFIGDIR and CLIENTSECRETS are
factored in to GC_Values[GC_CLIENT_SECRETS_JSON]. On a new
installation, it will have the value
os.path.join(GM_Globals[GM_GAM_PATH], FN_CLIENT_SECRETS_JSON) but on an
existing installation the environment variables may have it located
somewhere else.
2016-11-03 13:50:43 -04:00
Jay Lee
aa1db89bd3 include gam-setup.bat in MSI and zip. Call it from MSI at end of install. 2016-11-03 13:29:26 -04:00
Jay Lee
ff3a8644ec Merge branch 'master' of https://github.com/jay0lee/GAM 2016-11-03 12:49:32 -04:00
Jay Lee
4721469b1d prettify oauth2.txt 2016-11-03 12:49:19 -04:00
Jay Lee
c4a3d29964 Easier to copy scope list 2016-11-03 12:48:46 -04:00
Jay Lee
1454526e65 gam-setup.bat file to automate Windows setup
part of #309
2016-11-03 11:38:23 -04:00
Ross Scroggs
2c0026512d Fix doGamVersion (#311)
* Fix doGamVersion

doGamVersion is called by showUsage with checkForArgs=False to keep
doGamVersion from wandering through some other command’s arguments

* Update documentation

* Further doGamVersion cleanup

Allow simple and check, keep pylint happy

* gam version simple is script only option, not exposed to user
2016-11-02 17:30:54 -04:00
Jay Lee
3c85da292e GAM 4.01 2016-11-02 15:28:15 -04:00
Jay Lee
c7b5251b03 prompt for admin email and use as hint 2016-11-02 15:23:43 -04:00
Jay Lee
5307a560bd fix service account create call 2016-11-02 14:45:17 -04:00
Jay Lee
059e6a1813 fix gam version 2016-11-02 13:30:42 -04:00
Jay Lee
395a561b8c GAM v4.0 2016-11-02 13:17:44 -04:00
Jay Lee
6c3a744ed3 improve project create instructions. 2016-11-02 13:15:10 -04:00
Jay Lee
907126d642 gam version simple, save project files to GAM path 2016-11-02 12:59:38 -04:00
Jay Lee
9e4506141e add message to restart term 2016-11-02 12:43:40 -04:00
Jay Lee
5deac72484 authorize admin and check service account 2016-11-02 11:40:00 -04:00
Jay Lee
9def6e6d73 run alias command. 2016-11-02 11:02:11 -04:00
Jay Lee
fefe9de384 further improvements to scope check. 2016-11-02 10:20:27 -04:00
Jay Lee
f8341be9ea add statement about extract starting. 2016-11-02 09:47:12 -04:00
Jay Lee
3c50f464cc add -p argument to disable profile update 2016-11-02 09:33:46 -04:00
Jay Lee
6961a0e1b3 more details on check serviceaccount 2016-11-01 22:32:33 -04:00
Jay Lee
3fa6cde6b0 another change to improve missing scope errors 2016-11-01 22:23:34 -04:00
Jay Lee
4129e05f5e check serviceaccount command, better error on missing service scopes 2016-11-01 22:13:45 -04:00
Jay Lee
42137297a1 extra space 2016-11-01 12:17:46 -04:00
Jay Lee
bc64e9a67c fix color escaping on MacOS 2016-11-01 12:16:49 -04:00
Jay Lee
e1ec8b8649 fix color function calls 2016-11-01 12:07:51 -04:00
Jay Lee
b9ec06807b message color and more error checking. 2016-11-01 12:01:33 -04:00
Jay Lee
e3d826cdb3 catch tar errors and exit. 2016-11-01 11:53:30 -04:00
Jay Lee
4306dba9f1 Merge branch 'master' of https://github.com/jay0lee/GAM 2016-11-01 11:44:30 -04:00
Jay Lee
cf397c228c MacOS >= 10.10 please 2016-11-01 11:44:05 -04:00
Jay Lee
a2a6719333 Update macos-build.sh 2016-10-31 14:57:21 -04:00
Jay Lee
7cef626a6f Add GAM to Windows PATH 2016-10-31 14:45:31 -04:00
Jay Lee
be44ae4322 newlines and stuff 2016-10-31 13:33:33 -04:00
Jay Lee
4c00b54ad4 no trailing slash 2016-10-31 13:30:22 -04:00
Jay Lee
8508ee4afa force to lower 2016-10-31 13:15:39 -04:00
Jay Lee
17b2c4091d login_hint, not hint 2016-10-31 12:23:45 -04:00
Jay Lee
925f4532bc IndexError, not KeyError 2016-10-31 12:20:04 -04:00
Jay Lee
786bbe5609 handle ToS not accepted yet for project create 2016-10-31 12:17:43 -04:00
Jay Lee
6be52c8b3c MacOS doesn't seem to like ~ as /home/jay 2016-10-31 09:37:17 -04:00
Jay Lee
2c2046a784 arguments and further improvements to gam-install.sh 2016-10-31 09:29:50 -04:00
Jay Lee
20de452685 Linux and MacOS install script 2016-10-31 05:57:47 -04:00
Jay Lee
df603937ee fix service account url display 2016-10-29 14:56:33 -04:00
Jay Lee
315a1db144 Merge branch 'master' of https://github.com/jay0lee/GAM 2016-10-29 14:09:42 -04:00
Jay Lee
968c096a99 "gam create project" rough draft. Needs a lot of work but first steps towards solving #309 2016-10-29 14:09:08 -04:00
Jay Lee
6703519d36 remove email-audit include 2016-10-28 12:07:53 -04:00
Jay Lee
9dd8696c1e remove email-audit include 2016-10-28 12:07:28 -04:00
Jay Lee
df7c12b737 Remove email-audit include 2016-10-28 12:07:06 -04:00
Jay Lee
7cfba0ada1 remove email-audit-v1.json file 2016-10-28 12:06:16 -04:00
Jay Lee
2ee5109424 remove email-audit API mapping 2016-10-28 12:05:44 -04:00
Jay Lee
721f787f0f Update macos-gam.spec 2016-10-28 10:56:08 -04:00
Jay Lee
bc62f7a9f6 Update linux-gam.spec 2016-10-28 10:55:37 -04:00
Jay Lee
4eee744321 what's new in 3.8 2016-10-28 10:08:35 -04:00
Jay Lee
0dbd460b71 GAM 3.8 2016-10-28 09:47:08 -04:00
Ross Scroggs
5c27695613 Handle newlines in CrOS notes field; fix print tokens to show clientId (#308)
* Handle newlines in CrOS notes field

* Fix gam print tokens to show clientId

* Generalize print.show tokens
2016-10-27 13:08:49 -04:00
Ross Scroggs
e52e939afa Documentation update (#305) 2016-10-24 17:47:55 -04:00
Ross Scroggs
60a97b3e26 Make show filelist/filetree consistent (#304)
show filetree shows all files
show filelist shows owned files

This change makes them both show owned files by default. The anyowner
argument makes either command show all files
2016-10-24 17:33:26 -04:00
Ross Scroggs
fb41698e5f Improve user custom schema field handling (#303)
Allow entire schemas or schema fields to be cleared on update
Allow specification of multivalued schema field type on update
2016-10-24 16:39:40 -04:00
Ross Scroggs
9397c1c07c Update methods to cancel guardian invitations (#302)
* Update methods to cancel guardian invitations

Add command to delete guardian invitation by invitationId
Update delete guardian to allow deleting invitation only

* Normalize invited email address in print guardians
2016-10-24 15:05:01 -04:00
Ross Scroggs
071231310c Add orderby argument to show filetree/filelist (#300)
* Add orderby to show filetree/filelist

* Add orderby argument to show filetree/filelist
2016-10-24 06:40:25 -04:00
Ross Scroggs
1c92ad3d6d Clean up, small fixes (#299)
* Clean up, small fixes

* Update as per Jay's suggestions

* Add Jays'changes

# Conflicts:
#	src/gam.py
2016-10-23 20:33:10 -04:00
Jay Lee
facd10d882 Get GitHub raw body_text instead of Markdown 2016-10-23 15:50:09 -04:00
Jay Lee
e634a04dc7 Update README.md 2016-10-22 20:55:05 -04:00
Jay Lee
f291e9c4d3 deprecate appspot in favor of GitHub 2016-10-22 16:52:47 -04:00
Ross Scroggs
ed48e5ecd9 Version (#297)
* Use string version rather than float; add check to gam version

This lets you have 3.72.0 or 3.72a; current scheme continues to work as
well.

Handle bad data in lastupdatecheck.txt

$ gam version check
GAM 3.72 - http://git.io/gam
Jay Lee <jay0lee@gmail.com>
Python 2.7.12 64-bit final
google-api-python-client 1.5.2
Darwin-15.6.0-x86_64-i386-64bit x86_64
Path: /Users/admin/Documents/GoogleApps/GAM
Version: Check, Current: 3.72, Latest: 3.71
$

Note that latest version at appspot is behind

* Don't allow check argument from showUsage
2016-10-22 11:52:00 -04:00
Ross Scroggs
34e55a492e Clan up App ID handling (#296)
Drop Google+ in name to id direction, transfers no longer supported
Keep Google+ in id to name direction for printing old transfers
Show real app name to user
Don't bail on unknown app ID from Google, keep going by using
'applicationId: ####' as name
2016-10-21 18:06:53 -04:00
Ross Scroggs
abf3d8fa1c Make set/show signature consistent. (#292)
* gam user foo signature should set signature on primary address for backwards compatability

gam user foo show signature already does this

* Simply signature/show signature, don't loop to get primary
2016-10-19 22:24:20 -04:00
Ross Scroggs
6143d00476 Align commands, delete debug prints (#291) 2016-10-16 11:07:36 -04:00
Ross Scroggs
a2fe11fd70 Use callGAPIitems to clean up code (#290) 2016-10-16 09:05:13 -04:00
Ross Scroggs
78ce646eee Add fullquery to show filelist (#289)
This allows user to bypass "'me' in owners"
2016-10-15 21:23:40 -04:00
Ross Scroggs
3115c578a0 Put send_email back for todrive, update OAUTH2_SCOPES (#288)
* Put send-email back for todrive, update scopes

* Use loop instead of list comprehension for selected_scopes

* Engage brain fully before commit
2016-10-15 20:37:28 -04:00
Jay Lee
5eb029baf1 Fix some pylint warnings/errors 2016-10-13 20:37:01 -04:00
Jay Lee
cf0af20d89 remove no-longer-used geturl() function 2016-10-13 20:09:22 -04:00
Jay Lee
ce067c3e2d URITemplate 3.0 2016-10-13 15:33:10 -04:00
Jay Lee
801078e618 Upgrade httplib2 to 10/13/16 HEAD for a few fixes 2016-10-13 15:19:35 -04:00
Jay Lee
dabb3362c8 formatting fixes 2016-10-13 15:09:35 -04:00
Jay Lee
8d2af5ca66 Kill GData until it's dead
Fixes #148. May need further cleanup
2016-10-13 14:52:25 -04:00
Jay Lee
31cdf55af8 Google API Client 1.5.2 2016-10-13 06:29:03 -04:00
Ross Scroggs
c4dffd7584 Fix typos (#286) 2016-10-13 05:43:32 -04:00
Ross Scroggs
14c86e3780 Fix typo (#285) 2016-10-12 21:23:21 -04:00
Ross Scroggs
cc1126ff2f Eliminate unused body item (#283) 2016-10-12 18:44:30 -04:00
Ross Scroggs
6b0fb9e54d Handle misidentified codepoint (#281) 2016-10-12 18:02:26 -04:00
Jay Lee
8435d41d44 mention binary builds 2016-10-12 16:01:54 -04:00
Jay Lee
e92b9690d4 require Python 2.7 2016-10-12 16:01:54 -04:00
Jay Lee
953ea36177 Merge pull request #280 from PMox/master
Remove underscore or the string will not match
2016-10-12 15:27:49 -04:00
Paolo Mossino
36d0d7da7b Remove underscore or the string will not match 2016-10-12 21:18:51 +02:00
Jay Lee
e101231f9b Update linux-build.sh 2016-10-12 14:53:41 -04:00
Jay Lee
3090c59582 GAM 3.72 2016-10-12 14:19:07 -04:00
Jay Lee
1de7e32f25 fix error message and remove old comment 2016-10-12 14:10:22 -04:00
Jay Lee
2bf058b089 make sure courseId isn't already prefixed before prefixing 2016-10-12 13:34:59 -04:00
Jay Lee
c507714ab6 CrOS device actions (disable, deprovision, reenable...) 2016-10-12 11:56:31 -04:00
Jay Lee
cdc72d2f23 catch additional errors on guardian delete 2016-10-12 11:53:37 -04:00
Jay Lee
2368adfebd Update .gitignore to block MSI and WIX build files 2016-10-12 11:26:51 -04:00
Jay Lee
46c000f6d1 MSI Windows build 2016-10-12 11:25:26 -04:00
Jay Lee
4254ed4d86 x64 7zip, clean pyinsataller, python-64 path 2016-10-11 15:56:24 -04:00
Jay Lee
3233807c17 remove pathext from macos-gam.spec 2016-10-11 15:30:40 -04:00
DevicesMac
b3501a2d4a MacOS PyInstaller build files 2016-10-11 10:30:12 -04:00
Jay Lee
3d5ac7c448 windows-gam.spec rename 2016-10-11 09:48:20 -04:00
Jay Lee
11e2a3845d Rename gam.spec to windows-gam.spec 2016-10-11 09:47:32 -04:00
Jay Lee
9812c23513 Rename build.bat to windows-build.bat 2016-10-11 09:47:09 -04:00
Jay Lee
5c02c1b50c Merge pull request #266 from taers232c/master
Fix typo, clean up doPrintGroupMembers
2016-09-01 17:30:40 +00:00
Ross Scroggs
f3f3f91fcb Fix spacing 2016-09-01 10:10:19 -07:00
Ross Scroggs
f8be56efdc Fix type, clean up doPrintGroupMembers 2016-09-01 10:07:26 -07:00
Jay Lee
dcae0155b2 Simply and generalize print group-members so that fields like the new status are automatically included as Google updates API 2016-08-31 14:23:58 -04:00
Jay Lee
7c21c670dc Merge pull request #264 from taers232c/master
Add new fields to list of selectable fields in print groups
2016-08-31 12:59:19 +00:00
Ross Scroggs
44c0d4bf15 Add new fields to list of selectable fields in print groups 2016-08-30 19:05:51 -07:00
Jay Lee
39159d3ce7 Merge pull request #262 from taers232c/master
Make routine to get sendas/signature attributes
2016-08-31 01:59:29 +00:00
Ross Scroggs
62565d7c97 Update documentation 2016-08-30 16:48:28 -07:00
Ross Scroggs
b8b268f0ad default argument allowed for signature and sendas 2016-08-30 16:45:58 -07:00
Ross Scroggs
4182b3cd1e Print heading for info sendas 2016-08-30 12:08:37 -07:00
Ross Scroggs
24c0390174 Make routine to get sendas/signature attributes; allow treatasalias for gam signature 2016-08-30 11:52:27 -07:00
Jay Lee
2b336d8c9f Merge pull request #261 from taers232c/master
Cleanup documentation
2016-08-30 18:09:19 +00:00
Ross Scroggs
5b02a22d77 Cleanup documentation 2016-08-30 10:57:21 -07:00
Jay Lee
b680a6bf12 Merge pull request #260 from taers232c/master
Help for the Python challenged
2016-08-30 14:29:01 +00:00
Ross Scroggs
2b65d87bfa Help for the Python challenged 2016-08-30 07:23:52 -07:00
Jay Lee
e214ad8154 Merge pull request #259 from taers232c/master
Statement misplaced in doPrintCourseParticipants; correct documentation
2016-08-30 14:14:04 +00:00
Ross Scroggs
3bf2ecc6e3 Statement misplaced in doPrintCourseParticipants; correct documentation 2016-08-29 21:37:20 -07:00
Jay Lee
594e10d0a4 Merge pull request #258 from taers232c/master
Allow charset <CharSet> with gam batch, like gam csv
2016-08-29 20:03:03 +00:00
Ross Scroggs
656f87f89c Allow charset <CharSet> with gam batch, like gam csv 2016-08-29 12:46:03 -07:00
Jay Lee
a5dd9b69e3 Merge pull request #257 from taers232c/master
Handle orphaned Org Unit/Role Ids in print admins/adminroles
2016-08-29 17:18:28 +00:00
Ross Scroggs
ae8949c6d6 Handle orphaned OrgUnit/Role Ids 2016-08-27 08:39:42 -07:00
Ross Scroggs
eeb29355f1 Merge remote-tracking branch 'jay0lee/master' 2016-08-27 08:37:58 -07:00
Jay Lee
4dea7cb650 add issue template back 2016-08-27 11:35:27 -04:00
Jay Lee
da4177f0e4 Merge remote-tracking branch 'origin/master' into branch-3.63
Attempting to move branch-3.63 to master
2016-08-27 11:22:45 -04:00
Jay Lee
c7b44ae8d9 Merge pull request #255 from taers232c/branch-3.63
Update create datatransfer
2016-08-27 15:18:48 +00:00
Ross Scroggs
37726c493d Update create datatransfer
Allow drive as a synonym for Drive, avoids an API call
Update documentation
2016-08-27 05:50:59 -07:00
Jay Lee
83af5ee757 Merge pull request #250 from taers232c/branch-3.63
Clean up gam print guardians
2016-08-27 12:37:04 +00:00
Jay Lee
c35df829b0 3.71 whatsnew.txt update 2016-08-25 12:22:56 -04:00
Jay Lee
1539c37576 GAM 3.71 2016-08-25 12:16:43 -04:00
Ross Scroggs
34b9029e90 Clean up print guardians
Make gam show guardians and gam print guardians
show guardians gives formatted output
print guardians gives CSV output
This is like all other print/show xxx commands.
2016-08-21 22:41:01 -07:00
Jay Lee
50abefd5f9 Merge pull request #249 from taers232c/branch-3.63
Documentation update
2016-08-21 16:57:53 -04:00
Ross Scroggs
0508554e2c Documentation update 2016-08-21 13:46:09 -07:00
Jay Lee
67ab67c81b Merge pull request #248 from taers232c/branch-3.63
Rewrite of doRequestOauth
2016-08-21 16:17:58 -04:00
Ross Scroggs
7bf263fd35 Rewrite of doRequestOauth
This does not handle overwrite
2016-08-21 12:57:22 -07:00
Jay Lee
88067e64e6 Merge pull request #246 from taers232c/branch-3.63
Pythonic way to initialize dictionary entry that may/may not already exist
2016-08-21 15:35:04 -04:00
Ross Scroggs
8254b1040c Pythonic way to initialize dictionary entry 2016-08-21 12:29:10 -07:00
Jay Lee
fdaf6508b1 Merge pull request #245 from taers232c/branch-3.63
Clean up create/update user schema
2016-08-21 15:25:07 -04:00
Ross Scroggs
bbc9b8fb2b Clean up create/update user schema
Pass True/False to indicate update/create
Delete extraneous () with del, del is an operator, not a function
When looking for field to delete, don't distract user with messages
saying field name doesn't match other fields; you get a message only if
you give and invalid field name
Error messages to user on invalid arguments say create/update as
appropriate, before it always said create which might be confusing if
you just did an update
It's an error to say deletefield when creating
2016-08-21 11:46:42 -07:00
Jay Lee
f229ab7de2 Merge pull request #244 from taers232c/branch-3.63
Guardian command cleanup
2016-08-21 13:54:33 -04:00
Ross Scroggs
3b5b5be881 Guardian command cleanup
Upshift states list as a convenience to user; split on comma or space
as in most other lists
Tell user about invalid argument
Keep pylint happy, eliminate extra spaces
2016-08-21 10:47:57 -07:00
Jay Lee
b9155f42d6 Merge pull request #243 from taers232c/branch-3.63
Improve CSV file reading
2016-08-21 12:59:24 -04:00
Ross Scroggs
aa48edc0ff Improve CSV file reading 2016-08-21 09:54:27 -07:00
Jay Lee
2f306d1181 Upgrade to RSA 3.4.2 2016-08-20 16:53:46 -04:00
Jay Lee
0e01a32c64 Upgrade to six 1.10. 2016-08-20 16:48:16 -04:00
Jay Lee
a1b2e3b63b Upgrade to googleapiclient 1.5.1 2016-08-20 16:46:12 -04:00
Jay Lee
219509853f upgrade to oauth2client 3.0. Override some strings in GAM
to reduce custom changes to oauth2client.
2016-08-20 16:25:49 -04:00
Jay Lee
f66aa71ec1 Schema updates no longer deletes fields. Fixes #229. 2016-08-19 15:58:56 -04:00
Jay Lee
e1e49f8edf fixes #238
-make sure name exists in body before setting givenName or familyName.
-change if else for updateCmd to avoid a double negative
2016-08-19 12:44:27 -04:00
Jay Lee
bbb70421ed what's new with you? 2016-08-18 08:15:58 -04:00
Jay Lee
7a2785ba5c -Classroom Guardian API commands
-Allow pulling new activity reports w/o code changes
-GAM 3.7 baby!
2016-08-18 08:05:16 -04:00
Jay Lee
2c15230756 Merge pull request #214 from taers232c/branch-3.66
Multiple fixes and enhancements
2016-08-17 20:09:02 -04:00
Ross Scroggs
232755590d Write all thread messages to stderr 2016-08-12 09:50:44 -07:00
Ross Scroggs
ddd040e395 Make calendar showacl usable 2016-08-12 09:02:00 -07:00
Ross Scroggs
a9dc255979 Update gam print courses, gam update group, gam print mobile 2016-08-01 15:35:38 -07:00
Ross Scroggs
785073eb87 Fix cut/paste error 2016-08-01 08:25:07 -07:00
Ross Scroggs
368a6d1217 Add drivefilename <DriveFileName> to gam get drivefile 2016-08-01 08:19:42 -07:00
Ross Scroggs
13d2f9dd96 Clean up error messages; make subroutine to get file attributes; format takes list in get drivefile 2016-08-01 07:42:37 -07:00
Ross Scroggs
e85e54b86a Simplify exception declarations 2016-07-30 08:29:22 -07:00
Ross Scroggs
e39d441d01 Add required JSON files 2016-07-29 18:18:43 -07:00
Ross Scroggs
f6f15c8801 Many changes: Gmail API, CSV files, print commands 2016-07-29 17:52:42 -07:00
Ross Scroggs
0dc8accb64 Tweak {RT} pattern 2016-06-30 09:31:26 -07:00
Ross Scroggs
79964892f6 Update documentation 2016-06-30 07:52:56 -07:00
Ross Scroggs
b648ddbfdf Implement Steve Main template processing suggestion. 2016-06-29 23:06:56 -07:00
Ross Scroggs
cab068357a Handle UTF in formatted multiline signature 2016-06-29 08:18:03 -07:00
Ross Scroggs
acb4b39953 Add format argument to gam show signature 2016-06-29 07:45:10 -07:00
Ross Scroggs
74481b1c31 Correct documentation 2016-06-28 18:19:06 -07:00
Ross Scroggs
aa9cd3f1ca Correct documentation 2016-06-28 18:12:02 -07:00
Ross Scroggs
7f67a3043a Add replace argument to gam signature/vacation to allow replacement in data read from file 2016-06-28 18:05:11 -07:00
Ross Scroggs
734413266b Update documentation 2016-06-28 06:03:32 -07:00
Ross Scroggs
159d4cf085 Add gam update group <Group> clear [member] [manager] [owner] 2016-06-27 23:19:12 -07:00
Ross Scroggs
4b380be637 Improve audit date handling 2016-06-25 13:28:25 -07:00
Ross Scroggs
0f84ee1e07 Trivial fix 2016-06-25 13:09:30 -07:00
Ross Scroggs
38df869afc Update documentation 2016-06-25 13:06:14 -07:00
Ross Scroggs
c05c2ffaa2 Handle unknown UID in datatransfer commands 2016-06-22 05:52:47 -07:00
Ross Scroggs
bb86db3cd3 Update documentation 2016-06-16 11:39:28 -07:00
Ross Scroggs
9e2e9632c2 Cleanup gam delete/update drivefileacl; admin-settings API is v2 2016-06-16 11:36:25 -07:00
Ross Scroggs
2b686ebe74 Cleanup gam delete/update drivefileacl 2016-06-16 07:30:25 -07:00
Ross Scroggs
15043d1de8 Update documentation 2016-06-15 15:47:38 -07:00
Ross Scroggs
e00151ef39 Minor cleanup 2016-06-14 14:15:20 -07:00
Ross Scroggs
160ea6aa5b Update list of fields in gam show filelist , add field selection to gam show fileinfo 2016-06-14 14:10:23 -07:00
Ross Scroggs
7e3297e8c7 Update documentation 2016-06-10 06:15:56 -07:00
Ross Scroggs
880a9c8939 Update documentation 2016-06-09 07:32:58 -07:00
Ross Scroggs
64f9cbd54f Update documentation 2016-06-07 06:30:29 -07:00
Ross Scroggs
6b97662f8a Exclude tcl/tk module 2016-06-03 18:12:32 -07:00
Ross Scroggs
71f37acdf4 Minor cleanup 2016-05-27 15:52:02 -07:00
Ross Scroggs
a58c300e84 Update documentation 2016-05-25 16:43:49 -07:00
Ross Scroggs
b1ce6544f5 Allow aliasdomain as a synonym for domainalias in gam delete
This makes gam delete consistent with gam create and gam info
2016-05-25 16:22:09 -07:00
Ross Scroggs
d5daf24c15 Update documentation 2016-05-23 17:04:46 -07:00
Ross Scroggs
9b9aea9841 Add show counts argument to show labels to get message counts 2016-05-23 16:39:00 -07:00
Ross Scroggs
1145faef56 Handle both date error messages in gam report 2016-05-23 15:26:18 -07:00
Ross Scroggs
e42ca916fe Add inherit to gam create org 2016-05-22 20:28:25 -07:00
Ross Scroggs
24611614d6 Add domain and member arguments to gam print group-members 2016-05-21 10:57:25 -07:00
Ross Scroggs
6df45551e5 Show summary override if present in showCalendars 2016-05-16 13:15:09 -07:00
Ross Scroggs
7c211c664f Increment counter in updateCalendar 2016-05-16 12:36:52 -07:00
Ross Scroggs
f07a2ef2be Update showDriveFiles to handle lists containing dictionaries 2016-05-14 16:34:04 -07:00
Ross Scroggs
3df6d1f833 Update documentation 2016-05-13 12:02:29 -07:00
Ross Scroggs
f6396d5e4a Cleanup handling sorted CSV titles 2016-05-13 08:51:55 -07:00
Ross Scroggs
8db8c48fd9 Update documentation 2016-05-12 22:58:44 -07:00
Ross Scroggs
8cc4710874 Add gam <Users> show gplusprofile
Clean up show gmailprofile; messages to stderr, emailAddress is first
column

Miscellaneous cleanup
2016-05-12 22:33:19 -07:00
Ross Scroggs
d42f4d764a Clean up API calls
callGData, callGAPI, callGAPIpages: Eliminate service= and function=;
they're not needed
callGAPIpages: eliminate default for items, add u'items' where
required, eliminate items=
2016-05-12 07:01:21 -07:00
Ross Scroggs
21715f079b Clean up gam batch, optimize gam csv processing 2016-05-12 06:38:51 -07:00
Ross Scroggs
b345cb063f Rename user_batch_size to batch_size (not used in this version, future compatability) 2016-05-12 06:24:35 -07:00
Ross Scroggs
d6fdaa2874 Make my_customer default CUSTOMER_ID 2016-05-12 06:13:09 -07:00
Ross Scroggs
daf2735eb3 Fix error in downloadDriveFile: extension checking was incorrect 2016-05-11 23:16:38 -07:00
Ross Scroggs
68ad322f41 Update documentation 2016-05-03 15:58:16 -07:00
Ross Scroggs
f688404ac6 Update documentation 2016-05-03 13:57:26 -07:00
Ross Scroggs
13a9024dd1 Update documentation; code cleanup, fixes and minor enhancements to match master branch
Add u’ in numerous places
Relocate and tweak win32_unicode_args
Improve _DeHTMLParser to handle notification printing better
Add group|groups and mobile to gam report
Add UTF8 conversion in getDelegates
Delete extra enclosing loop in showCalSettings
Add todrive to gam print adminroles
Add id: to OrgUnitID field in gam print admins
getSignature will print None when no signature is defined
2016-05-02 19:41:13 -07:00
Ross Scroggs
2fdfc3750d Include commands documentation 2016-04-30 07:41:12 -07:00
Ross Scroggs
9246aed660 Print formatted permission on add/update drivefileacl instead of JSON data 2016-04-29 13:01:36 -07:00
Ross Scroggs
0efb736b42 Have showDriveFileInfo and showDriveFileRevisions use print_json; clean up print_json 2016-04-29 07:05:40 -07:00
Ross Scroggs
ef7fcb2114 Add revision argument to gam get drivefile; add command gam show filerevisions 2016-04-28 14:38:19 -07:00
Ross Scroggs
52f4c049e1 Handle invalid printer ID in gam print printjobs/gam printjob fetch 2016-04-28 07:20:27 -07:00
Ross Scroggs
ee00a41ff9 Eliminate offset/maxresults arguments for gam print printjobs/gam printjob fetch 2016-04-27 10:27:33 -07:00
Ross Scroggs
289fda94df Add maxresults argument to gam print printjobs/gam printjob fetch 2016-04-27 06:58:54 -07:00
Ross Scroggs
49d1845129 Update offset/limits processing for print jobs 2016-04-26 14:51:45 -07:00
Ross Scroggs
d850f5575b Add offset, limit arguments to print printjobs and printjob fetch
gam print printjobs ... offset <Integer> limit <Integer>
 gam printjob <PrinterId> fetch ... offset <Integer> limit <Integer>
2016-04-26 10:27:34 -07:00
Ross Scroggs
1f2ffcf97a Handle UTF8 user name in show drivefileacl 2016-04-20 16:05:06 -07:00
Ross Scroggs
4ea63c3167 Add skus <SKUIDList> to gam info user 2016-04-20 12:57:21 -07:00
Ross Scroggs
f2887abb49 Fix error in app2appID 2016-04-20 09:13:59 -07:00
Ross Scroggs
3ada129e7f Add update drive file changes from master pull 212 2016-04-19 12:30:42 -07:00
Ross Scroggs
6c3a0e2b71 Handle info user/group arguments better in whatis 2016-04-15 15:06:34 -07:00
Ross Scroggs
457feac4ac Cosmetic change 2016-04-13 11:50:40 -07:00
Ross Scroggs
45728fbbda Set character set based on OS 2016-04-08 10:57:39 -07:00
Ross Scroggs
0d507855bd Add fields argument to gam print group-members 2016-04-08 05:37:39 -07:00
Ross Scroggs
5fa2f3d955 Add membernames to gam print group-members 2016-04-06 13:54:07 -07:00
Ross Scroggs
bfc734138d Fix create alias target where target is a group 2016-04-06 07:52:53 -07:00
Ross Scroggs
fdfa830aa0 Handle language with admin settings 2016-04-04 06:19:55 -07:00
Ross Scroggs
d78ea8efeb Drop name, admin_secondary_email from update instance 2016-04-04 05:20:32 -07:00
Ross Scroggs
d47f5967e9 Improve info/update customer, revert some update instance code
Move print customer info code from getInstanceInfo to getCustomerInfo;
getInstanceInfo calls getCustomerInfo

doUpdateCustomer already had code added to doUpdateInstance, remove
from doUpdateInstance
2016-04-03 22:50:11 -07:00
Ross Scroggs
2debf9507a Fix errors 2016-04-03 21:09:33 -07:00
Ross Scroggs
e527cd42a1 Change POSTAL to ADDRESS 2016-04-03 21:02:37 -07:00
Ross Scroggs
38399c0e1e Improve info/update instance
Print address fields in logical order in gam info instance

Allow multiple address fields to be entered in a single gam update
instance command
2016-04-03 20:44:34 -07:00
Jay Lee
43782ca3f8 further updates to replace Admin Settings GData API with customers() 2016-04-03 18:59:25 -04:00
Jay Lee
b5f911e259 Replace some old admin settings API calls with customers(). 2016-04-03 13:15:24 -04:00
Jay Lee
4596accf5a Merge pull request #212 from Yuyuu/master
Allow to search drive files to update by query
2016-04-03 12:41:20 -04:00
Jay Lee
d4cad7a242 Merge pull request #213 from taers232c/branch-3.66
Calendar add/update/show improvements
2016-04-03 12:40:43 -04:00
Ross Scroggs
9ba732e0bf Make getCalendarAttributes to eliminate duplicate code
Add clear|none as option for reminder/notification in add/update
calendar to allow clearing all reminders/notifications

Add domain to user in update calendar

Add domain to calendarId in update calendar
2016-04-01 14:39:23 -07:00
Vincent Tertre
0034704b3f Handle query for drive file update 2016-03-30 17:07:05 +02:00
Ross Scroggs
b6caa8a5ba Put normalize calendarId back 2016-03-27 08:09:06 -07:00
Jay Lee
cca9684ffb Merge pull request #209 from taers232c/branch-3.65
Clean up, fixes
2016-03-25 20:06:34 +00:00
Jay Lee
c00259a5b6 remove check for full email in Calendar add ACL, we check later 2016-03-25 14:19:33 -04:00
Ross Scroggs
455730dad8 Clean up stderr error messages 2016-03-24 18:28:06 -07:00
Ross Scroggs
b17b80ee12 Put back GAM specific no browser message 2016-03-21 15:05:27 -07:00
Ross Scroggs
4fadf68da4 Improve signature/vacation file handling
When gam signature/vacation specify a file for input, allow optional
charset <Charset> argument.
2016-03-19 11:15:35 -07:00
Ross Scroggs
fb9aebf123 Improve signature/vacation file handling
Strip BOM_UTF8 from encoded data read by readFile; Windows puts a
BOM_UTF8 in UTF-8 files.
2016-03-19 10:34:49 -07:00
Ross Scroggs
087c6775e3 Label processing cleanup 2016-03-17 17:47:05 -07:00
Ross Scroggs
a3c509ce61 Cosmetic cleanup 2016-03-17 16:22:15 -07:00
Ross Scroggs
6b0fce21a5 Cache oauth2service.json data 2016-03-17 11:09:27 -07:00
Ross Scroggs
7b8b4674e7 Cleanup, fixes, bump version
doProcessMessages: clean up parsing, slight code reorder
readFile: add encoding parameter
doSignature: read file with encoding
doVacation: read file with encoding
2016-03-17 10:09:25 -07:00
Jay Lee
4956873357 two more removes 2016-03-16 13:27:20 -04:00
Jay Lee
4dd1d6a244 more bad files removed 2016-03-16 13:26:08 -04:00
Jay Lee
65367947e0 Add pyasn1 and rsa for native crypt operations. Remove bad admx stuff 2016-03-16 13:24:19 -04:00
Jay Lee
a83414f831 bump to 3.65 2016-03-16 13:14:28 -04:00
Jay Lee
bba894bdc3 Remove Drive-storage and Coordinate SKUs (faster) and add Apps-Lite. 2016-03-16 13:13:24 -04:00
Jay Lee
190c4f212d Upgrade googleapiclient to 1.5.0 2016-03-16 13:05:55 -04:00
Jay Lee
d8a78d96ae Upgrade to oauth2client 2.0.1. Remove support for seperate pem keys. 2016-03-16 12:53:05 -04:00
Jay Lee
1d30eb7d91 switch "delete messages" to use batchDelete() for performance 2016-03-16 12:21:50 -04:00
Jay Lee
ac86758e79 Add messages modify command which can add/remove labels on messages. 2016-03-16 12:04:59 -04:00
Jay Lee
afee6c32a3 Merge pull request #189 from taers232c/branch-3.63
Fixes; cosmetic changes
2016-03-16 10:47:54 -04:00
Ross Scroggs
7412236679 gam <UserTypeEntity> untrash message|messages query <Query> [doit] [max_to_untrash <Number>] 2016-03-09 15:41:04 -08:00
Ross Scroggs
3ef433687a Add [listlimit <Number>] to gam print cros
This limits the number of entries shown for activeTimeRanges and
recentUsers
2016-03-09 15:14:26 -08:00
Ross Scroggs
82d43d0b62 Minor tweaks for compatibility 2016-03-05 18:40:01 -08:00
Ross Scroggs
bf31e72384 Add noaliases and groups arguments to gam info group 2016-03-04 14:29:56 -08:00
Ross Scroggs
2a37589a9f Add untrash argument to gam delete drivefile 2016-03-03 08:25:03 -08:00
Jay Lee
a334645910 ISSUE_TEMPLATE is shown in plaintext on new issues 2016-02-29 09:07:09 -05:00
Jay Lee
6519a5b007 First shot at an issue template
goal is to reduce # of "how do I" issues which should go to discussion
forum as well as bug reports which don't include full details.
2016-02-29 09:03:14 -05:00
Ross Scroggs
cafa01248a Cosmetic cleanup 2016-02-27 19:56:04 -08:00
Ross Scroggs
5ab14fef05 Handle missing values in column 2016-02-27 19:07:01 -08:00
Ross Scroggs
6b6ada5b2c Improve error message 2016-02-26 19:10:15 -08:00
Ross Scroggs
18420275af Fix setting GamPath in Windows (better solution) 2016-02-26 17:00:46 -08:00
Ross Scroggs
8f283acf66 Fix setting GamPath in Windows 2016-02-26 16:47:08 -08:00
Ross Scroggs
f27df74339 Allow csv FileName:FieldName and csvfile FileName:FieldName
csvfile form must be used for: gam csvfile FileName:FieldName … as gam
csv FileName is already defined
2016-02-24 16:25:57 -08:00
Ross Scroggs
ca059a62a6 Strip blanks, handle empty entries in gam file and gam csvfile 2016-02-12 06:03:20 -08:00
Ross Scroggs
6dae2302c0 Fix doVacation 2016-02-11 20:41:14 -08:00
Ross Scroggs
bae5f20ec4 gam csv FileName:FieldName changed to gam csvfile FileName:FieldName
Added error checking
2016-02-11 20:09:40 -08:00
Jay Lee
51a4d92a90 3.63, maxresults param to print groups 2016-02-05 10:51:33 -05:00
Jay Lee
d527f4104f simplify file list of users and add csv list 2016-02-05 10:31:31 -05:00
Jay Lee
2d74916ca5 group membership sync batch support 2016-02-05 09:42:07 -05:00
Jay Lee
0aabe4ae9b Merge pull request #179 from taers232c/master
Optionally get admin email address from command line in doRequestOauth
2016-01-27 14:33:30 -05:00
Ross Scroggs
0a41b4ec68 Optionally get admin email address from command line in doRequestOauth 2016-01-27 10:41:17 -08:00
Ross Scroggs
9cf4a151aa Merge remote-tracking branch 'jay0lee/master' 2016-01-27 07:38:02 -08:00
Jay Lee
10fcf566b8 fix todrive for "gam print adminroles" 2016-01-27 09:32:48 -05:00
Jay Lee
2704a8b695 groups and mobile reports
gam report groups
gam report mobile  (always blank?)
2016-01-27 09:25:21 -05:00
Jay Lee
09814b7dcd Merge pull request #166 from taers232c/master
Dynamic scope repair/cleanup
2016-01-27 09:18:48 -05:00
Jay Lee
4f2ce2625d Merge pull request #176 from jeremi/fix_set_vacation
Setting up vacation is not working anymore
2016-01-27 09:15:28 -05:00
Ross Scroggs
9c368b7d10 Fix handling of nonexistent extra-args.txt 2016-01-22 07:25:01 -08:00
jeremi
f9bd5506c7 Setting up vacation was not working anymore
The underlying function enable as a UpdateVacation is exception a boolean and not a string.
2016-01-22 10:25:59 +08:00
Ross Scroggs
4a168d16a3 Get environment variables, signal files via table; Fix update groups to allow IDs 2016-01-15 14:28:19 -08:00
Ross Scroggs
b817bd04ec Handle "all users in domain" member in doPrintGroups
Get id in members list, use that if there is no email. If neither email
or id exist, give the “Not sure…” message
2016-01-14 15:29:57 -08:00
Ross Scroggs
43adae4e70 Eliminate superflous exception 2016-01-14 07:38:51 -08:00
Ross Scroggs
77ebba9c62 Drop temporary environment variable GAM_ADMIN
Admin email address comes from user via prompt, stored in gamscopes.json
2016-01-14 06:14:51 -08:00
Ross Scroggs
ee517c1800 Do better sort of API names in doRequestOAuth 2016-01-14 05:53:28 -08:00
Ross Scroggs
154099c3f4 Back to single scopes list 2016-01-13 21:32:51 -08:00
Ross Scroggs
1746845651 Handle Unicode in doGetNotifications 2016-01-13 08:07:44 -08:00
Ross Scroggs
d07ab2d7e1 Clean up string quotes 2016-01-12 19:33:11 -08:00
Ross Scroggs
f3b970ae14 getGDataOAuthToken (formerly tryOAuth) was wiping out additional_headers 2016-01-12 15:34:25 -08:00
Ross Scroggs
16d8cddf12 In gam print admins, include id: with orgUnitId in CSV file 2016-01-12 07:27:31 -08:00
Ross Scroggs
671f7d810c buildOrgUnitIdToNameMap only mapped top level org units 2016-01-11 23:14:24 -08:00
Ross Scroggs
0d94dd3fa5 Handle out of domain users better 2016-01-11 22:21:01 -08:00
Ross Scroggs
eb5cfde630 Handle JSON format errors 2016-01-11 10:06:53 -08:00
Ross Scroggs
aa04e3ec1d Fix select_default_scopes in doRequestOAuth
https://www.googleapis.com/auth/admin.directory.user.security is not a
subset of https://www.googleapis.com/auth/admin.directory.user
2016-01-11 08:35:01 -08:00
Ross Scroggs
0333e29eef Service creation, dynamic scope cleanup
Make routine getAPIversionHttpService to handle all steps to get a
service.
Make routine handleOAuthTokenError to handle OAuth token errors.
In doRequestOAuth, get admin email address from oauth2.txt if it
exists, otherwise prompt for it.
Move reading of gamscopes,json into SetGlobalVariables as a local
routine _getScopesAdminDomainFromGamScopesJson.
2016-01-10 09:58:21 -08:00
Ross Scroggs
8929ee534f Ease transition to new all service model
Get admin email address and domain from oauth2.txt if it exists
2016-01-07 13:07:43 -08:00
Ross Scroggs
8eb347488f Eliminate 'email' scope, may sure that selected_scopes has unique elements 2016-01-06 06:24:01 -08:00
Ross Scroggs
f8642a18df Delete 'email' scope
If this ‘email’ scope is included here then it better be output in the
scopes list in doRequestOAuth otherwise nothing works. Why was it even
here?
2016-01-05 12:06:18 -08:00
Ross Scroggs
12ccd58eae Fix error 2016-01-05 08:54:14 -08:00
Ross Scroggs
95bb288e38 Back to scopes by api 2016-01-05 08:18:19 -08:00
Ross Scroggs
5c64f0825f Fix UTF error in getDelegates 2016-01-04 13:10:25 -08:00
Ross Scroggs
25e97b97d4 Sort scopes in doRequestOauth, move scope over for readability 2016-01-04 12:57:23 -08:00
Ross Scroggs
d258d4da63 Refactor doRequestOAuth
The selected scopes list can't be created until completion; otherwise
turning off a scope in one API turns it off in all other APIs.

Elimination of child scopes is still supported in select-default.

Top level API list is sorted.
2016-01-04 12:23:54 -08:00
Ross Scroggs
cb79688a73 in doRequestOAuth, sort list by API title 2016-01-04 10:52:33 -08:00
Ross Scroggs
dae75f6234 Fix typo in email-audit-v1.jsom 2016-01-04 06:02:10 -08:00
Ross Scroggs
0209b51c4d Clean up no setCurrentAPIScopes, api, version not needed
No scopes error message changed to take API title
2016-01-04 05:48:07 -08:00
Ross Scroggs
f35c188496 Finish refactoring doRequestOAuth, make selected_scopes a set 2016-01-03 23:24:40 -08:00
Ross Scroggs
c00d820c75 Eliminate extraneous loop 2016-01-03 21:42:32 -08:00
Ross Scroggs
69689e286b Fix setCurrentAPIScopes to work for both buildGAPIObject and OAuthInfo 2016-01-03 20:39:34 -08:00
Ross Scroggs
5697580bd0 Fix typo 2016-01-03 20:21:33 -08:00
Ross Scroggs
2288e99e56 Dynamic scope cleanup 2016-01-03 20:19:40 -08:00
Ross Scroggs
c8fd6e76af Merge remote-tracking branch 'jay0lee/master' 2016-01-03 16:04:56 -08:00
Ross Scroggs
23cb9afec7 Merge remote-tracking branch 'jay0lee/master'
# Conflicts:
#	src/gam.py
2016-01-03 16:04:44 -08:00
Jay Lee
20e8175ae1 further updates to allow scopes to be saved as list
also determine default scopes based on substrings
2016-01-03 14:10:20 -05:00
Ross Scroggs
3a38dceb5f Dynamic scope repair
The last changes broke GData services and gam oath info
2016-01-03 10:00:12 -08:00
Ross Scroggs
c885cdefbb Merge remote-tracking branch 'jay0lee/master' 2016-01-03 07:16:04 -08:00
Jay Lee
b7d5374718 refactor saving of scopes to be a single list, not per API
-allows for APIs which have overlapping scopes (Drive, Apps Activity)
-buildGAPIObject() can dynamically decide which scopes to ask for based
on:
-scopes that API claims to use (discovery document)
-scopes that user has prevously selected
-still need to fixup GData API calls to make do same.
2016-01-03 10:11:12 -05:00
Jay Lee
59a0aadd72 ignore gamscopes.json 2016-01-03 09:20:56 -05:00
Jay Lee
a3f218a98d Merge pull request #164 from taers232c/master
Dynamic scopes update, other cleanup
2016-01-03 09:18:36 -05:00
Ross Scroggs
0424ced649 Reorder cancel and continue/back in doRequestOAuth, add missing .keys() 2016-01-02 13:35:08 -08:00
Ross Scroggs
9f75968684 More clean up of doRequestOAuth 2016-01-02 12:38:45 -08:00
Ross Scroggs
90fd503838 If GA_DOMAIN not set, derive domain from GAM_ADMIN if defined
If neither set, we have to decide what to do.
2016-01-02 11:59:37 -08:00
Ross Scroggs
a7a3f2eef6 Clean up input handling in doRequestOauth 2016-01-02 09:52:10 -08:00
Ross Scroggs
008a65329e gam oauth info now verifies scopes 2016-01-02 09:10:22 -08:00
Ross Scroggs
2390c4284e Clearing cache_dir has to come after directory check 2016-01-02 07:44:48 -08:00
Ross Scroggs
75483185d6 Clean up
Clean up handling of missing json files
Clean up processing environment variables, signal files
2016-01-02 07:09:15 -08:00
Ross Scroggs
acb21cb926 Clean signature printing 2016-01-01 14:39:10 -08:00
Ross Scroggs
c0ee674060 Full scopes list causes internal error, back (for now) to API specific list 2016-01-01 14:04:01 -08:00
Ross Scroggs
7d69c8e3bd Dynamic scopes cleanup
I deleted the  'email' scope because it caused everything to fail as
it's not (and never was) authorized. What was/is it for?
2016-01-01 12:13:02 -08:00
Ross Scroggs
38a37c49de Merge remote-tracking branch 'jay0lee/master' 2016-01-01 08:11:01 -08:00
Jay Lee
a36478b1f5 Merge pull request #163 from taers232c/master
Dynamic scopes first steps
2016-01-01 11:09:56 -05:00
Ross Scroggs
bcb17cd0a5 in doRequestOauth, initialize API use_scopes from gamscopes.json if available 2016-01-01 07:40:22 -08:00
Ross Scroggs
1ec164a25a Bring gam oauth delete back, give creation message 2016-01-01 06:50:16 -08:00
Ross Scroggs
571a9dcb3e Handle no scopes for API
This should probably call doRequestOAuth rather than bailing out
2015-12-31 23:37:15 -08:00
Ross Scroggs
a0ac6265e9 If SetGlobalVariables calls doRequestOAuth, don't call again if command is oauth create 2015-12-31 23:18:44 -08:00
Ross Scroggs
ea6f49f7be Have OAuthInfo print API scope table 2015-12-31 22:54:07 -08:00
Ross Scroggs
0470680a4d Add some error checking for gamscopes.json 2015-12-31 20:43:33 -08:00
Ross Scroggs
e0c52c8660 First cut, dynamic scopes
Environment variable GAMSCOPESFILE points to scopes file.
Scopes file gamscopes.json
2015-12-31 20:20:38 -08:00
Ross Scroggs
3182ce031c Leave email scope in tryOAuth 2015-12-31 18:00:29 -08:00
Ross Scroggs
f6dd0ccd12 Cleanup doRequestOAuth
admin_email isn't used; If it's going to be used I'd say:
`
if GC-Values{GC_ADMIN):
  admin_email = GC_Values[GC_ADMIN]
 else:
  admin_email = raw_input(u'Please enter your admin email address: ')
`
'oauth2' isn't in API_VER_MAPPING so the remove fails. If it might go
back in but you don't want it here, say:
`
if u'oauth2' in apis:
  apis.remove(u'oauth2')
`
2015-12-31 17:30:39 -08:00
Ross Scroggs
775b0c8c60 Merge remote-tracking branch 'jay0lee/master' 2015-12-31 16:43:57 -08:00
Jay Lee
1c4424dd0b Get doRequestOAuth to actually print out list of scopes 2015-12-31 16:47:53 -05:00
Jay Lee
8501aec7bc gdata discovery file description updates 2015-12-31 12:47:03 -05:00
Jay Lee
05a36d3245 update Google OAuth URIs
upstream pull request is:
https://github.com/google/oauth2client/pull/368
2015-12-31 12:44:40 -05:00
Ross Scroggs
2bf8f9164e Fix typo, drop unneeded API from table 2015-12-31 08:59:38 -08:00
Ross Scroggs
56732ea3e8 Commit Jay's changes 2015-12-31 08:18:28 -08:00
Ross Scroggs
7a4b32aadb Merge remote-tracking branch 'jay0lee/master'
# Conflicts:
#	src/gam.py
2015-12-31 08:10:35 -08:00
Jay Lee
16add1bf24 UBER_SCOPE for appsactivity-v1 API 2015-12-31 09:34:03 -05:00
Jay Lee
433cdfe87d define UBER_SCOPES to cut down on unnecessary scope overlap 2015-12-31 08:57:16 -05:00
Jay Lee
a3d0a0250a Remove usage of oauth2 API for now as it's nearly useless 2015-12-31 08:52:05 -05:00
Ross Scroggs
b037333d2b Clean up OAuthInfo 2015-12-31 01:01:05 -08:00
Ross Scroggs
bf6c2ef266 buildGAPIObject reworked
This gets everything working but does not address the issue of matching
the admins actual scope list.

You'd better define GA_DOMAIN as it used to come out of oauth2.txt if
it wasn't defined.

I had to add Audit API and Site Verification API to the service account
list of APIs and downloaded a new oauth2service.json.
2015-12-30 23:39:04 -08:00
Ross Scroggs
abde922b49 Only build object if necessary 2015-12-30 15:38:39 -08:00
Ross Scroggs
eca89ca5e9 Fix typo 2015-12-30 15:17:43 -08:00
Ross Scroggs
a91c987107 Merge remote-tracking branch 'jay0lee/master' 2015-12-30 14:49:32 -08:00
Jay Lee
12166e2245 more work on dynamic scope selections 2015-12-30 17:10:06 -05:00
Ross Scroggs
e612c20141 Merge remote-tracking branch 'jay0lee/master' 2015-12-30 13:44:32 -08:00
Jay Lee
d2039e5566 cleanup admin settings API name 2015-12-30 16:24:37 -05:00
Jay Lee
20bba75e41 default scope selections
basic logic is:
-if 1 scope for API, use it
-skip over scopes ending in .readonly, .action or .verify_only UNLESS
all scopes are readonly, use all scopes (this is case with reports api)
-all other scopes are used by default.
2015-12-30 16:12:49 -05:00
Ross Scroggs
2d26b647c8 Merge remote-tracking branch 'jay0lee/master' 2015-12-30 12:54:40 -08:00
Ross Scroggs
ab0bec7a7b Merge remote-tracking branch 'jay0lee/master'
# Conflicts:
#	src/gam.py
2015-12-30 12:54:35 -08:00
Jay Lee
b6c5f1b1e7 gdata discovery files wrap into Windows build 2015-12-30 15:39:40 -05:00
Jay Lee
881cc4d255 redo of Ross' latest patch against my updates 2015-12-30 15:36:24 -05:00
Jay Lee
3d61973071 discovery files for 2 other gdata apis to standardize scope discovery 2015-12-30 15:30:58 -05:00
Ross Scroggs
5ae1f3c441 Cleanup 2015-12-30 11:50:17 -08:00
Ross Scroggs
6ae4cf495d Merge remote-tracking branch 'jay0lee/master' 2015-12-30 11:39:07 -08:00
Jay Lee
900fc9c4e3 sanitize some defaults 2015-12-30 14:22:33 -05:00
Ross Scroggs
0207e84551 Merge remote-tracking branch 'jay0lee/master' 2015-12-30 11:11:11 -08:00
Jay Lee
3a2d663c86 Merge pull request #158 from taers232c/master
Fix typo, delete call to obsolete function
2015-12-30 14:10:25 -05:00
Ross Scroggs
7aaaaf9125 Fix type, delete obsolete function 2015-12-30 10:59:04 -08:00
Ross Scroggs
e051b9bffa Merge remote-tracking branch 'jay0lee/master' 2015-12-30 10:32:09 -08:00
Jay Lee
df0bcda952 Merge pull request #157 from taers232c/master
no_browser.txt still needed by output_csv
2015-12-30 13:28:49 -05:00
Jay Lee
d871378336 dynamically get API scopes for "gam oauth request" 2015-12-30 13:25:06 -05:00
Ross Scroggs
f99add7a3f no_browser.txt still needed by output_csv 2015-12-30 10:20:18 -08:00
Jay Lee
7515700b1a fix GC_Defaults dict init 2015-12-30 13:18:56 -05:00
Ross Scroggs
99db4d50d3 Merge remote-tracking branch 'jay0lee/master' 2015-12-30 10:06:41 -08:00
Jay Lee
0cd8246bdb remove unused G+ APIs 2015-12-30 12:58:21 -05:00
Ross Scroggs
29ee81ef18 Merge remote-tracking branch 'jay0lee/master' 2015-12-30 09:57:46 -08:00
Jay Lee
9e09c06770 gdata scopes as globals 2015-12-30 12:57:42 -05:00
Jay Lee
65603ca314 Merge pull request #156 from taers232c/master
Use GAM_ADMIN environment variable as short-term fix to hardcoded value
2015-12-30 12:56:50 -05:00
Ross Scroggs
a983d23f91 Define GAM_ADMIN environment variable 2015-12-30 09:52:18 -08:00
Ross Scroggs
3545306559 Merge remote-tracking branch 'jay0lee/master' 2015-12-30 09:23:02 -08:00
Jay Lee
08163cc5cd remove some legacy variables 2015-12-30 12:13:57 -05:00
Jay Lee
c629c3424c TODOs for a few ugly hard coded hacks 2015-12-30 12:08:37 -05:00
Jay Lee
ef403119d9 Intial steps to switch to 100% service accounts
Still need to store admin email and granted scopes in a file as well as
build these on first run.
2015-12-30 11:30:03 -05:00
Ross Scroggs
798c126881 Merge remote-tracking branch 'jay0lee/master' 2015-12-30 06:43:08 -08:00
Jay Lee
813d503bb8 use object already avail instead of buildDiscoveryObject() 2015-12-30 09:15:34 -05:00
Jay Lee
7e71a06c5f Merge pull request #155 from taers232c/master
Cleanup and gam csv optimization
2015-12-30 09:02:20 -05:00
Ross Scroggs
68475a00c1 Merge remote-tracking branch 'origin/master' 2015-12-26 08:46:48 -08:00
Ross Scroggs
4d71b6943c Clean up, gam csv optimization
showCalSettings had nested loop over user
doLabel cleaned up, has starting point for parsing arguments passed in
from main
doUpdateUser has starting point for parsing arguments passed in from
main
gam csv processing is optimized. All '~~xxx~~' and '~xxx' substitutions
are found for each argument and loaded into a dictionary once before
the loop. During the loop, the substitutions can be performed quickly.
2015-12-26 08:46:42 -08:00
Ross Scroggs
42caddb8a3 Merge remote-tracking branch 'jay0lee/master' 2015-12-25 05:50:03 -08:00
Ross Scroggs
52d8604099 Merge remote-tracking branch 'jay0lee/master' 2015-12-25 05:48:41 -08:00
Jay Lee
2df3aef52d more fixes to doVacation 2015-12-25 05:42:47 -05:00
Jay Lee
9773e25932 Merge pull request #154 from taers232c/master
Fix doVacation bug
2015-12-25 05:37:45 -05:00
Ross Scroggs
cd766d90e4 Fix doVacation problem 2015-12-24 23:04:40 -08:00
Ross Scroggs
8f69fc84a8 Merge remote-tracking branch 'jay0lee/master' 2015-12-23 12:49:36 -08:00
Jay Lee
a8f0882220 Merge pull request #153 from taers232c/master
Cleanup argument passing to callGAPI, callGAPIpages, callGData
2015-12-23 15:48:22 -05:00
Ross Scroggs
d79c28d2d3 Eliminate unneeded service=, function=, items= in calls to API functions
Saves 5.5kb!
2015-12-23 11:07:08 -08:00
Ross Scroggs
ba756d12b2 Merge remote-tracking branch 'jay0lee/master' 2015-12-23 08:15:34 -08:00
Jay Lee
48e6872233 Merge pull request #150 from taers232c/master
Debugging only incompatible with gam batch - and gam csv -
2015-12-23 11:08:04 -05:00
Ross Scroggs
5037a9bbfd Debugging only incompatible with gam batch - and gam csv - 2015-12-23 07:35:36 -08:00
Jay Lee
3fcde95fe8 Add "gam print roles" 2015-12-23 09:52:04 -05:00
Ross Scroggs
a58e5e4276 Merge remote-tracking branch 'jay0lee/master' 2015-12-23 06:42:45 -08:00
Jay Lee
2235c10df7 handle blank lines it batch 2015-12-23 09:05:03 -05:00
Jay Lee
327e09291b disable discovery cache
Disable discovery cache as it broke CSV commands on windows with lock
errors. The cache is new in googleapiclient 1.4.2 which was upgraded
after GAM 3.61:

30125120b4

down the line, we should investigate actual issue with cache as enabling
it would improve GAM performance.
2015-12-23 08:06:22 -05:00
Jay Lee
1dd36424be showLabels handle non-Gmail users 2015-12-23 06:55:22 -05:00
Jay Lee
ade2d0ae54 handle no results on Gmail profile 2015-12-23 06:50:57 -05:00
Jay Lee
ac3dbd25f3 If userid isn't in domain, return blank 2015-12-23 06:32:43 -05:00
Jay Lee
2e6811d2d4 Limit cache filenames to 64 chars to prevent long paths from confusing windows 2015-12-23 06:26:45 -05:00
Jay Lee
61a9d0c0a6 Merge pull request #146 from taers232c/master
Global variables update
2015-12-23 06:18:36 -05:00
Ross Scroggs
4cc775bcae Eliminate try except IndexError in argument parsing 2015-12-22 23:27:42 -08:00
Ross Scroggs
96dfa52dba Clean up parsing transferSecCals, transferDriveFiles 2015-12-22 22:16:33 -08:00
Ross Scroggs
1f1329c536 Ok, allow gam create admin to take uid: or id:abcdefghi for org unit 2015-12-22 20:41:13 -08:00
Ross Scroggs
3bb54f875d In gam create admin, org unit id is id:abcdefghj not uid:abcdefhgi 2015-12-22 20:34:42 -08:00
Ross Scroggs
84b6c1cb87 Handle undefined role 2015-12-22 18:15:15 -08:00
Ross Scroggs
4f4bb316d0 Handle invalid argument in gam print admins 2015-12-22 18:09:22 -08:00
Ross Scroggs
fe6430edc6 Global variables do-over
Global variables not from environment variables/signal files in
GM_Globals
Global variables from environment variables/signal files in GC_Values
SetGlobalVariables processes all environment variables/signal files
buildGAPIObject reworked
buildGAPIServiceObject reworked
Switch resource calendar processing from GData to GAPI
Implement role assignments
2015-12-22 17:56:37 -08:00
Ross Scroggs
6c421de8c4 Merge remote-tracking branch 'jay0lee/master' 2015-12-22 17:52:31 -08:00
Ross Scroggs
c04ae91dc5 Commit Jay changes 2015-12-22 17:37:56 -08:00
Ross Scroggs
14bc340e56 Commit Jays changes 2015-12-22 17:23:43 -08:00
Ross Scroggs
6fd107c230 CSV debug check inadvertently dropped 2015-12-22 16:39:56 -08:00
Ross Scroggs
5f2c2103a5 Revamp environment variables/signal files processing
Global variables not from environment variables/signal files in
GM_Globals
Global variables from environment variables/signal files in GC_Values
SetGlobalVariables processes all environment variables/signal files
buildGAPIObject reworked
buildGAPIServiceObject reworked
Switch resource calendar processing from GData to GAPI
Implement role assignments
2015-12-22 16:10:30 -08:00
Jay Lee
a06828dbcf Delete messages fixes
-catch more unauthorized service account errors
-print which account we're working with before attempting auth and
mailbox search
-soft errors on API call to keep GAM from quiting on mailbox error.
-
2015-12-22 16:42:47 -05:00
Jay Lee
b260cb8f50 add admin commands, upgrade resources to new API, partial text subst. on csv commands 2015-12-22 15:25:58 -05:00
Jay Lee
2fc3355388 Merge pull request #144 from taers232c/master
More cleanup
2015-12-21 14:50:37 -05:00
Ross Scroggs
24d5a169f6 Standardize server not found error handling 2015-12-19 16:17:09 -08:00
Ross Scroggs
2bc0dd5fbe More cleanup
Handle debug.gam, extra_args.txt in one place
Clean up coding for callGAPIpages
Simplify coding for disable_ssl_certificate_validation
Break up long arg lists into multiple lines for callGData, callGAPI,
callGAPIpages
Handle missing client_secrets.json inline in doRequestOauth
2015-12-19 16:05:43 -08:00
Jay Lee
d76e5008a7 Merge pull request #134 from taers232c/master
Cleanup - use named constants
2015-12-19 06:56:08 -05:00
Ross Scroggs
21f01757a3 Make sure that callGData and callGAPI return something 2015-12-17 17:11:17 -08:00
Ross Scroggs
4da936344f Downshift more Boolean values 2015-11-24 18:10:23 -08:00
Ross Scroggs
05920cc7d7 Boolean values were not downshifted in doCreateUser 2015-11-24 17:14:37 -08:00
Ross Scroggs
edb17ad06e Allow reader in doCalendarAddACL, fix infinite loops in doPop, doCreateUser, doUpdateUser 2015-11-24 16:51:06 -08:00
Ross Scroggs
ab6f8fa7bf Update doVacation 2015-11-22 10:57:00 -08:00
Ross Scroggs
b822608b15 Define/use file processing routines; fix show filelist allfields
Define and use new file handling primitives to simply code and isolate
error handling.

Error message cleanup.

Google added a new object (userPermission) to the filesResource object
which broke showDriveFiles because it has an item named 'id' which
wiped out the file id. This fix makes compound column names for all
dictionary objects except for labels.
2015-11-21 09:50:11 -08:00
Ross Scroggs
7a9bda9b1b Guard vacation info with convertUTF8, fix typo in doGetNotifications 2015-11-19 16:46:16 -08:00
Ross Scroggs
3edfce202f Cleanup - use named constants
Use named constants instead of repeated literals
Define messages at top, would make language translation easier
Clean up result handling in doDownloadActivity/ExportRequest
Handle missing Python SSL module in one place

The following two files need to be updated to 3.61
https://gam-update.appspot.com/latest-version-announcement.txt
https://gam-update.appspot.com/latest-version.txt
2015-11-16 07:13:39 -08:00
Jay Lee
c5b4a822d9 Merge pull request #133 from taers232c/master
Cleanup - consolidate redundant code, no functional change
2015-11-15 13:35:48 -05:00
Ross Scroggs
e3ae862732 Cleanup - consolidate redundant code, no functional change
Define GAM_URL, GAM_INFO, GAM_RELEASES strings once, use in multiple
places
Simplify getAPIVer, getAPIScope - use dictionaries
Define getServiceFromDiscoveryDocument to replace redundant code
Move tryOAuth/doRequestOAuth calls to commonAppsObjInit
Use dictionaries in appID2app/app2appID
2015-11-15 10:30:46 -08:00
Jay Lee
74b1127f6e Merge pull request #130 from taers232c/master
Apply convertUTF8 to more fields, clean up gam create resource/print resources
2015-11-15 09:30:51 -05:00
Ross Scroggs
2c3f12b38c Limit pageSize to 100 in doDriveActivity, otherwise backend errors 2015-11-14 12:09:52 -08:00
Ross Scroggs
94ee718aa9 Handle missing emails in courses. 2015-11-12 15:30:15 -08:00
Ross Scroggs
f34620aa73 Fix coding error getting environment variable GAM_AUTOBATCH 2015-11-12 12:40:46 -08:00
Ross Scroggs
a43bb56a43 Real fix to gam create resource 2015-11-11 06:35:50 -08:00
Ross Scroggs
a4ed95b81b Clean up gam create resource/print resources
Add type as a synonym of restype to create to make consistent with
update
Add type to print so that resource type is visible
2015-11-11 06:08:55 -08:00
Ross Scroggs
ddd8348bdd Apply convertUTF8 to more fields 2015-11-03 10:53:31 -08:00
Jay Lee
d32095f3fe Merge pull request #128 from taers232c/master
GAM 3.62 Bug Fixes, Clean Up
2015-10-30 08:46:02 -04:00
Ross Scroggs
a958bf8be7 Small cleanups 2015-10-29 16:02:24 -07:00
Ross Scroggs
2d643a551c Use update instead of patch in doUpdateOrg as patch doesn't work 2015-10-28 12:26:32 -07:00
Ross Scroggs
b16d75ec43 Fix .lower where () was missing 2015-10-28 11:54:36 -07:00
Ross Scroggs
f40af555c3 Use os.environ.get, remove try/except 2015-10-28 11:45:40 -07:00
Ross Scroggs
c82672d77b 3.62 Fixes 2015-10-27 18:30:07 -07:00
Jay Lee
867488bf77 Update README.md 2015-10-08 06:41:17 -04:00
Jay Lee
9c485334f1 Update README.md 2015-10-07 08:22:28 -04:00
Ross Scroggs
04ff83fc2d Correct gam update org <OrgUnit> add cros /all cros 2015-10-05 07:49:10 -07:00
Ross Scroggs
d61e2751ef Merge remote-tracking branch 'jay0lee/master' 2015-10-05 04:51:39 -07:00
Jay Lee
87b64572db Merge pull request #124 from taers232c/master
Changes to atom/service.py and gdata/service.py to support Domain Shared Contacts
2015-10-05 06:46:34 -04:00
Ross Scroggs
50a33a5083 gdata/service.py change not required 2015-10-04 08:23:00 -07:00
Ross Scroggs
2dc72ab262 Merge remote-tracking branch 'origin/master' 2015-10-03 21:25:41 -07:00
Ross Scroggs
6dec0ea0f1 Changes required for Domain Shared Contacts 2015-10-03 21:25:18 -07:00
Ross Scroggs
fdc4e867c2 Merge remote-tracking branch 'jay0lee/master' 2015-10-03 07:00:56 -07:00
Ross Scroggs
525731fe33 Merge remote-tracking branch 'jay0lee/master' 2015-10-01 08:01:47 -07:00
Jay Lee
66d86b8d4d Merge pull request #121 from taers232c/master
Clean up argument parsing, error messages, redundant use of .keys()
2015-10-01 05:38:16 -04:00
Ross Scroggs
2443d5d1cf Clean up argument parsing, error message, redundant use of .keys() 2015-09-30 14:40:05 -07:00
Jay Lee
8863e7337e Just get logo directly (reduce dependency on gdata library 2015-09-30 11:48:53 -04:00
Jay Lee
27b31ff1fb Add fixes back for oauth2client 2015-09-30 09:57:49 -04:00
Jay Lee
58335025c4 Make cloudprint. and admin-settings JSON files part of Windows binary 2015-09-30 09:42:08 -04:00
Jay Lee
c1225178d6 move nearly everything into /src to make git.io/gam cleaner w/o a readme tree 2015-09-30 09:14:32 -04:00
Jay Lee
8b19040e45 update googleapiclient, httplib2, oauth2client and passlib to latest versions 2015-09-30 09:07:28 -04:00
Jay Lee
6ba62b66b4 remove old setup files, sync gam.spec 2015-09-30 08:43:16 -04:00
Jay Lee
e1bd7d7ae9 Update README.md 2015-09-30 08:39:56 -04:00
Jay Lee
c9b2c1d8d6 Fix "gam update customer", customer field not needed. 2015-09-30 08:34:38 -04:00
Jay Lee
1ae2b960c7 update whatsnew.txt 2015-09-30 06:50:39 -04:00
Jay Lee
0c2d4ab5cb remove old duplicate doCreateDomain func and ver to 3.61 2015-09-30 06:46:07 -04:00
Jay Lee
b9460cbcea Merge pull request #118 from taers232c/master
Fixes
2015-09-30 06:44:34 -04:00
Ross Scroggs
886a26cc5d Fix doGetDomainInfo to properly pass logo <Filename> to doGetInstanceInfo 2015-09-29 22:54:29 -07:00
Ross Scroggs
94dcd98e8d Fixes
doUpdateCustomer - starting on wrong index
appID2add - wrong variable in error message
convertUserIDtoEmail - id is Python built-in, use uid
doPrintDataTransfers - todrive argument causes infinite loop
doPrintDataTransfers - trailing space
doGetDataTransferInfo - id is Python built-in, use dtId
2015-09-29 20:36:56 -07:00
Jay Lee
c3038807b9 Domains and Data Transfer APIs added 2015-09-29 15:52:05 -04:00
Jay Lee
7acb8a9e85 Merge pull request #103 from karlosss/patch-1
"updating user..." should be in stdout, not stderr
2015-08-21 19:04:33 -04:00
karlosss
2d47622a0e "updating user..." should be in stdout, not stderr
The "updating user..." info is pumped into stderr instead of stdout - i fixed this.
2015-08-22 00:59:57 +02:00
Jay Lee
bbfe5d36e8 Merge pull request #99 from taers232c/master
cleanup for pylint
2015-08-20 11:28:50 -04:00
Ross Scroggs
0ecb732d60 Remove appdirs 2015-08-20 07:45:17 -07:00
Ross Scroggs
8b5ac30030 Implement appdirs, cleanup for pylint 2015-08-19 23:31:23 -07:00
Jay Lee
0c7bf10355 one more by Ross 2015-07-11 20:29:48 -04:00
Jay Lee
319273eb03 large number of fixes from Ross Scroggs 2015-07-11 18:34:16 -04:00
Jay Lee
199c49ff5e course aliases, message delete/trash, misc other 2015-07-11 15:00:50 -04:00
Jay Lee
8d967b1125 minor fixes for GAM 3.5 Windows build 2015-07-02 06:38:13 -04:00
Jay Lee
277b5ac261 huge dump for Classroom, CloudPrint and batch fixes/improvements 2015-07-02 05:36:37 -04:00
Jay Lee
0b035deff0 update whatsnew.txt 2015-05-01 05:02:21 -04:00
Jay Lee
73c3cb013f be conservative with password hashing to prevent timeouts 2015-04-30 20:18:16 -04:00
Jay Lee
f2b2c90586 bump version to 3.45 2015-04-30 20:17:44 -04:00
Jay Lee
2c21aac0d6 Add six.py 1.9.0 for better OS X and Linux compatability 2015-04-30 14:35:16 -04:00
Jay Lee
ce1efd6cb9 what's new with you? 2015-04-15 15:07:01 -04:00
Jay Lee
5b72c7d713 update orgunit commands to support IDs 2015-04-15 15:06:48 -04:00
Jay Lee
2861b739c9 cleanup is_frozen for pyinstaller 2015-04-15 13:27:41 -04:00
Jay Lee
e5e9cd1367 cleanup "print cros" 2015-04-15 12:25:21 -04:00
Jay Lee
0fd9ab303d read extra-args.txt for additonal URL params 2015-04-15 11:48:16 -04:00
Jay Lee
db0dd231b1 googleapiclient 1.4 and oauth2client 1.4.7 upgrades 2015-04-15 11:47:26 -04:00
Jay Lee
a2e8d17a69 gafw, d4w and dfw license abbreviations 2015-04-15 10:52:09 -04:00
Jay Lee
e73eb0453d windows and pyinstaller ignores 2015-04-15 10:50:22 -04:00
Jay Lee
d9a911cf56 switch build.bat from py2exe to pyinstaller 2015-04-15 10:46:41 -04:00
Jay Lee
95c8b7ab16 direct load sha512_crypt so pyinstaller is happy 2015-04-15 10:30:29 -04:00
Jay Lee
825f16ecc7 Merge pull request #60 from erikpt/master
Add support for annotatedAssetId field on Chrome OS devices
2015-04-15 08:57:05 -04:00
Erik Pitti
a9993ad361 Fixed casing for annotatedAssetId field based on Google API docs. (Letter "D" in annotatedAssetId had been erroneously capitalized) 2015-04-13 17:35:31 -07:00
Erik Pitti
e2f717a46a Fixed spacing for orgs line 2015-04-13 13:29:07 -07:00
Erik Pitti
ec1b59066f Added support for annotatedAssetID field on updating Chrome OS devices 2015-04-13 13:22:14 -07:00
Jay Lee
3354bb386d bundle_files 3 to prevent crash of 32-bit Windows 2015-03-18 14:57:10 -04:00
Jay Lee
11bac44de6 3.43 whatsnew 2015-03-18 14:30:21 -04:00
Jay Lee
ee35a41d03 Revert "look for extra-args.txt file to read additional GAPI arguments from."
This reverts commit 6b2aa1c532.
2015-03-18 14:13:44 -04:00
Jay Lee
baf2e67744 Version 3.43 2015-03-18 13:56:14 -04:00
Jay Lee
0542a09b88 Have short URL use a key and catch any errors 2015-03-18 13:51:20 -04:00
Your Name
e37e6935c4 One liner fixes for calendar add/delete ACLs 2015-03-04 15:07:23 -05:00
Your Name
cbe848faff Merge branch 'master' of github.com:jay0lee/GAM 2015-03-04 14:59:20 -05:00
Your Name
6b2aa1c532 look for extra-args.txt file to read additional GAPI arguments from. 2015-03-04 14:58:46 -05:00
Jay Lee
9939fa0198 Merge pull request #24 from daethnir/set-unix-execute-bit
Adds execute bit to gam for unix/linux.
2014-12-03 08:57:02 -05:00
Bri Hatch
518b820ad5 Adds execute bit to gam for unix/linux. 2014-12-02 21:39:29 -08:00
Jay Lee
fd9a6b6737 fix permissionId handling on drive ACL delete/update 2014-11-21 08:40:00 -05:00
Jay Lee
3e8bb878c8 catch unauthorized client and show service account instructions 2014-11-21 08:39:27 -05:00
Jay Lee
25cd11e3a9 update whatsnew.txt for 3.42 2014-11-19 15:02:09 -05:00
Jay Lee
6e7c15d101 version bump to 3.42 2014-11-19 14:42:06 -05:00
Jay Lee
bc48432a8d return nice error if oauth2service.json missing 2014-11-19 12:16:04 -05:00
Jay Lee
723e8c042a get all groups for users with >200 2014-11-19 12:07:43 -05:00
Jay Lee
995f4db93b import string 2014-11-19 11:53:05 -05:00
Jay Lee
ac401bd1f2 remove oauth2client/anyjson.py 2014-11-19 11:50:17 -05:00
Jay Lee
9e2198e115 convert to string before urldecode 2014-11-19 11:49:46 -05:00
Jay Lee
eaf99c682f fix commit-batch on batch commands 2014-11-19 11:14:30 -05:00
Jay Lee
447a807f69 'gam license <sku>' to perform actions for users with given skus 2014-11-19 11:12:41 -05:00
Jay Lee
a88dde7d7a one more Code->GitHub URL 2014-11-19 11:06:19 -05:00
Jay Lee
2e28793663 gam print users ismailboxsetup 2014-11-19 11:05:56 -05:00
Jay Lee
dd90f6c0ad simplify cros/mobile info commands 2014-11-19 10:31:46 -05:00
Jay Lee
9c67b6b8b4 show gmailprofile command 2014-11-19 10:24:04 -05:00
Jay Lee
206b6864c7 user activity cleanup 2014-11-19 10:10:51 -05:00
Jay Lee
d65a2494bf remove stale comments 2014-11-19 10:07:58 -05:00
Jay Lee
d806d55a64 more consistency on user groupings ('all users', etc) 2014-11-19 10:06:32 -05:00
Jay Lee
05f5ed338b update all Google Code URLs to GitHub 2014-11-19 09:38:43 -05:00
Jay Lee
a119f77237 remove simplejson 2014-11-19 09:30:30 -05:00
Jay Lee
227985e8eb cleanup old apiclient files 2014-11-19 09:24:01 -05:00
Jay Lee
0ca14a918b upgrade googleapiclient and oauth2client versions 2014-11-19 09:22:13 -05:00
Jay Lee
71ade81064 no more simplejson 2014-11-19 09:13:25 -05:00
Jay Lee
c6012f049a 'gam <users> update labels' cleanup 2014-11-19 09:09:33 -05:00
Jay Lee
d05eab9f3f cleanup 2014-11-19 08:54:02 -05:00
Jay Lee
09816fa817 show more info in gam info cros 2014-11-19 08:53:19 -05:00
Jay Lee
3da941d8b4 gam mobile <id> action accountwipe 2014-11-19 08:52:01 -05:00
Jay Lee
8f2bc384bd OAuth Tokens Report 2014-11-19 08:48:29 -05:00
447 changed files with 148958 additions and 37 deletions

14
.github/ISSUE_TEMPLATE.txt vendored Normal file
View File

@@ -0,0 +1,14 @@
The issue tracker is for reporting product deficiencies. "How do I?" questions should be posted to the discussion forum at https://groups.google.com/group/google-apps-manager. When in doubt, start at the discussion forum and return here only when instructed to do so.
Please confirm the following:
* I have upgraded to the latest GAM release from https://git.io/gamreleases and I still have this issue.
* I am typing the command as described in the GAM Wiki at https://github.com/jay0lee/gam/wiki
Full steps to reproduce the issue:
1.
2.
3.
Expected outcome (what are you trying to do?):
Actual outcome (what errors or bad behavior do you see instead?):

91
.travis.yml Normal file
View File

@@ -0,0 +1,91 @@
if: tag IS blank
matrix:
include:
- os: linux
language: python
dist: precise
python:
- "2.7.15"
env:
- GAMOS=linux
- PLATFORM=x86_64
# - os: linux
# language: python
# dist: precise
# python:
# - "2.7.15"
# env:
# - GAMOS=linux
# - PLATFORM=i686
- os: osx
language: generic
osx_image: xcode10.1
env:
- GAMOS=macos
- PLATFORM=x64
addons:
homebrew:
packages:
python@2
- os: windows
language: shell
env:
- GAMOS=windows
PLATFORM=x64
CINST_ARGS=""
- os: windows
language: shell
env:
- GAMOS=windows
- CINST_ARGS=--forcex86
- PLATFORM=x86
before_install:
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then
powershell Install-WindowsFeature Net-Framework-Core;
cinst -y $CINST_ARGS python2;
export PATH=$PATH:/c/Python27/scripts;
cinst -y wixtoolset;
fi
- pip install pyinstaller
install:
- cd src
- pyinstaller --clean -F --distpath=gam $GAMOS-gam.spec
- gam/gam version
- export GAMVERSION=`gam/gam version simple`
- cp LICENSE gam
- cp whatsnew.txt gam
- cp GamCommands.txt gam
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then
cp gam-setup.bat gam;
GAM_ARCHIVE=gam-$GAMVERSION-$GAMOS-$PLATFORM.zip;
/c/Program\ Files/7-Zip/7z.exe a -tzip $GAM_ARCHIVE gam -xr!.svn;
mkdir gam-64;
cp -rf gam/* gam-64/;
/c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.11/bin/candle.exe -arch $PLATFORM gam.wxs;
/c/Program\ Files\ \(x86\)/Wix\ Toolset\ v3.11/bin/light.exe -ext /c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.11/bin/WixUIExtension.dll gam.wixobj -o gam-$GAMVERSION-$GAMOS-$PLATFORM.msi || true;
rm *.wixpdb;
else
GAM_ARCHIVE=gam-$GAMVERSION-$GAMOS-$PLATFORM.tar.xz;
tar cfJ $GAM_ARCHIVE gam/;
fi
script:
- gam/gam version
before_deploy:
- export TRAVIS_TAG="preview"
deploy:
provider: releases
api_key:
secure: bzambMcQwyv/o5c5GrKGCsZHgE5R85tg8sNFvPfpISz3+uosCjnBXas7wvCKzT75XUFi2ztfbYak6HdKf4sGnNHk0saEicB3slH+ghPyZbYzp76yvvduhFO2nWW3/F01tL+Yfqqt4/q8wFaWGjrC5km+6GLVyB4lWA/Uyu49qKnz02uSwyhBD/VFbO7DOQ65a1iWk9HngyMsu0Oi7HIbSjSLtxTHedNfOf3waW0NivTTxYXiYGX/MCu3GWhgIGj47a+H3A6FcQ/9QWvnKgnoixdgPBUz7kDb7ktsWwQsILPGStgH7iMuG49ZlXdEFmqwifBri2wvzmFEevBGZjHcupy1IGrNFRG+IUGKMotio+OkLHlLjuv7ZJtqCz/Vf5SNFgNyMSanx6jKEUJuYvndVg99IRXmYVwHFwPu5BAcJACpU6C0AfyGmmSqqwxCd46uXL62ynxNFpHuRfOqlDnmCTfZgjOciJSlDDpf+Xz9fF7+oCoeCi3mrcZVFjhd3tT6Oxw5HrsDtm0ZNld1cdLidaq8H6vOFgHMd0A9yNYZzTzXTvpmxzkXT4Zc7s+PYKN6z5fRZ+pJeckUjRXblvVEfs5HFSymavcOc5AkRwxpvOsTQMNmlnaJCBo5UNs0K/rVmRi5cFmaiwTcBCY0kTllOBJ4zWsfq8seiokWwNUNK2g=
file_glob: true
overwrite: true
file: gam-$GAMVERSION-*
skip_cleanup: true
draft: true
on:
repo: jay0lee/GAM

View File

@@ -1,42 +1,23 @@
GAM
============================
GAM is a free, open source command line tool for
Google Apps Administrators to manage
domain and user settings quickly and easily. GAM supports
* creating, deleting, and updating users, aliases, groups,
organizations, and resource calendars
* modifying user email settings such as IMAP, signatures,
vacation messages, profile sharing, email forwarding,
send as address, labels, and features.
* modifying calendar access rights for users and resource calendars.
* generating detailed reports for users, groups, resources,
account activity, email clients, and quotas.
* and many more commands
Downloads
---------
You can download the current GAM release from
the [GitHub Releases] page.
Documentation
------------------
GAM is a command line tool for Google G Suite Administrators to manage domain and user settings quickly and easily. [![Build Status](https://travis-ci.org/jay0lee/GAM.svg?branch=master)](https://travis-ci.org/jay0lee/GAM)
# Quick Start
## Linux / MacOS
Open a terminal and run:
```
bash <(curl -s -S -L https://git.io/install-gam)
```
this will download GAM, install it and start setup.
## Windows
Download the MSI Installer from the [GitHub Releases] page. Install the MSI and you'll be prompted to setup GAM.
# Documentation
The GAM documentation is hosted in the [GitHub Wiki]
# Mailing List / Discussion group
The GAM mailing list / discussion group is hosted on [Google Groups]. You can join the list and interact via email, or just post from the web itself.
# IM Room
[![Join the chat at https://gitter.im/jay0lee-GAM/community](https://badges.gitter.im/jay0lee-GAM/community.svg)](https://gitter.im/jay0lee-GAM/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
# Author
GAM is maintained by <a href="mailto:jay0lee@gmail.com">Jay Lee</a>. Please direct "how do I?" questions to [Google Groups].
Mailing List / Discussion group
-------------------------------
The GAM mailing list / discussion group is hosted
on [Google Groups]. You can join the list and interact
via email, or just post from the web itself.
Source Repository
-----------------
The official GAM source repository is on [GitHub] in the master branch.
Author
------
GAM is maintained by <a href="mailto:jay0lee@gmail.com">Jay Lee</a>.
[GAM release]: https://git.io/gamreleases
[GitHub Releases]: https://github.com/jay0lee/GAM/releases
[GitHub]: https://github.com/jay0lee/GAM/tree/master
[GitHub Wiki]: https://github.com/jay0lee/GAM/wiki/

72
src/.gitignore vendored Normal file
View File

@@ -0,0 +1,72 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
# C extensions
*.so
# Distribution / packaging
.Python
env/
bin/
build/
develop-eggs/
dist/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Rope
.ropeproject
# Django stuff:
*.log
*.pot
# Sphinx documentation
docs/_build/
# GAM specific
client_secrets.json
oauth2.txt*
oauth2service*
debug.gam
lastupdatecheck.txt
noupdatecheck.txt
nodito.txt
nobrowser.txt
nocache.txt
noverifyssl.txt
gamcache/
gam/
gam-64/
*.zip
*.msi
*.wixobj
*.wixpdb

1322
src/GamCommands.txt Normal file

File diff suppressed because it is too large Load Diff

547
src/LICENSE Normal file
View File

@@ -0,0 +1,547 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
APACHE HTTP SERVER SUBCOMPONENTS:
The Apache HTTP Server includes a number of subcomponents with
separate copyright notices and license terms. Your use of the source
code for the these subcomponents is subject to the terms and
conditions of the following licenses.
For the mod_mime_magic component:
/*
* mod_mime_magic: MIME type lookup via file magic numbers
* Copyright (c) 1996-1997 Cisco Systems, Inc.
*
* This software was submitted by Cisco Systems to the Apache Group in July
* 1997. Future revisions and derivatives of this source code must
* acknowledge Cisco Systems as the original contributor of this module.
* All other licensing and usage conditions are those of the Apache Group.
*
* Some of this code is derived from the free version of the file command
* originally posted to comp.sources.unix. Copyright info for that program
* is included below as required.
* ---------------------------------------------------------------------------
* - Copyright (c) Ian F. Darwin, 1987. Written by Ian F. Darwin.
*
* This software is not subject to any license of the American Telephone and
* Telegraph Company or of the Regents of the University of California.
*
* Permission is granted to anyone to use this software for any purpose on any
* computer system, and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The author is not responsible for the consequences of use of this
* software, no matter how awful, even if they arise from flaws in it.
*
* 2. The origin of this software must not be misrepresented, either by
* explicit claim or by omission. Since few users ever read sources, credits
* must appear in the documentation.
*
* 3. Altered versions must be plainly marked as such, and must not be
* misrepresented as being the original software. Since few users ever read
* sources, credits must appear in the documentation.
*
* 4. This notice may not be removed or altered.
* -------------------------------------------------------------------------
*
*/
For the modules\mappers\mod_imagemap.c component:
"macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com
For the server\util_md5.c component:
/************************************************************************
* NCSA HTTPd Server
* Software Development Group
* National Center for Supercomputing Applications
* University of Illinois at Urbana-Champaign
* 605 E. Springfield, Champaign, IL 61820
* httpd@ncsa.uiuc.edu
*
* Copyright (C) 1995, Board of Trustees of the University of Illinois
*
************************************************************************
*
* md5.c: NCSA HTTPd code which uses the md5c.c RSA Code
*
* Original Code Copyright (C) 1994, Jeff Hostetler, Spyglass, Inc.
* Portions of Content-MD5 code Copyright (C) 1993, 1994 by Carnegie Mellon
* University (see Copyright below).
* Portions of Content-MD5 code Copyright (C) 1991 Bell Communications
* Research, Inc. (Bellcore) (see Copyright below).
* Portions extracted from mpack, John G. Myers - jgm+@cmu.edu
* Content-MD5 Code contributed by Martin Hamilton (martin@net.lut.ac.uk)
*
*/
/* these portions extracted from mpack, John G. Myers - jgm+@cmu.edu */
/* (C) Copyright 1993,1994 by Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of Carnegie
* Mellon University not be used in advertising or publicity
* pertaining to distribution of the software without specific,
* written prior permission. Carnegie Mellon University makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied
* warranty.
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/*
* Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
*
* Permission to use, copy, modify, and distribute this material
* for any purpose and without fee is hereby granted, provided
* that the above copyright notice and this permission notice
* appear in all copies, and that the name of Bellcore not be
* used in advertising or publicity pertaining to this
* material without the specific, prior written permission
* of an authorized representative of Bellcore. BELLCORE
* MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
* OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS",
* WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
*/
For the srclib\apr\include\apr_md5.h component:
/*
* This is work is derived from material Copyright RSA Data Security, Inc.
*
* The RSA copyright statement and Licence for that original material is
* included below. This is followed by the Apache copyright statement and
* licence for the modifications made to that material.
*/
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
rights reserved.
License to copy and use this software is granted provided that it
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
Algorithm" in all material mentioning or referencing this software
or this function.
License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material
mentioning or referencing the derived work.
RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind.
These notices must be retained in any copies of any part of this
documentation and/or software.
*/
For the srclib\apr\passwd\apr_md5.c component:
/*
* This is work is derived from material Copyright RSA Data Security, Inc.
*
* The RSA copyright statement and Licence for that original material is
* included below. This is followed by the Apache copyright statement and
* licence for the modifications made to that material.
*/
/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
*/
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
rights reserved.
License to copy and use this software is granted provided that it
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
Algorithm" in all material mentioning or referencing this software
or this function.
License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material
mentioning or referencing the derived work.
RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind.
These notices must be retained in any copies of any part of this
documentation and/or software.
*/
/*
* The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0
* MD5 crypt() function, which is licenced as follows:
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
* ----------------------------------------------------------------------------
*/
For the srclib\apr-util\crypto\apr_md4.c component:
* This is derived from material copyright RSA Data Security, Inc.
* Their notice is reproduced below in its entirety.
*
* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
* rights reserved.
*
* License to copy and use this software is granted provided that it
* is identified as the "RSA Data Security, Inc. MD4 Message-Digest
* Algorithm" in all material mentioning or referencing this software
* or this function.
*
* License is also granted to make and use derivative works provided
* that such works are identified as "derived from the RSA Data
* Security, Inc. MD4 Message-Digest Algorithm" in all material
* mentioning or referencing the derived work.
*
* RSA Data Security, Inc. makes no representations concerning either
* the merchantability of this software or the suitability of this
* software for any particular purpose. It is provided "as is"
* without express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* documentation and/or software.
*/
For the srclib\apr-util\include\apr_md4.h component:
*
* This is derived from material copyright RSA Data Security, Inc.
* Their notice is reproduced below in its entirety.
*
* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
* rights reserved.
*
* License to copy and use this software is granted provided that it
* is identified as the "RSA Data Security, Inc. MD4 Message-Digest
* Algorithm" in all material mentioning or referencing this software
* or this function.
*
* License is also granted to make and use derivative works provided
* that such works are identified as "derived from the RSA Data
* Security, Inc. MD4 Message-Digest Algorithm" in all material
* mentioning or referencing the derived work.
*
* RSA Data Security, Inc. makes no representations concerning either
* the merchantability of this software or the suitability of this
* software for any particular purpose. It is provided "as is"
* without express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* documentation and/or software.
*/
For the srclib\apr-util\test\testmd4.c component:
*
* This is derived from material copyright RSA Data Security, Inc.
* Their notice is reproduced below in its entirety.
*
* Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All
* rights reserved.
*
* RSA Data Security, Inc. makes no representations concerning either
* the merchantability of this software or the suitability of this
* software for any particular purpose. It is provided "as is"
* without express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* documentation and/or software.
*/
For the srclib\apr-util\xml\expat\conftools\install-sh component:
#
# install - install a program, script, or datafile
# This comes from X11R5 (mit/util/scripts/install.sh).
#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission. M.I.T. makes no representations about the
# suitability of this software for any purpose. It is provided "as is"
# without express or implied warranty.
#
For the test\zb.c component:
/* ZeusBench V1.01
===============
This program is Copyright (C) Zeus Technology Limited 1996.
This program may be used and copied freely providing this copyright notice
is not removed.
This software is provided "as is" and any express or implied waranties,
including but not limited to, the implied warranties of merchantability and
fitness for a particular purpose are disclaimed. In no event shall
Zeus Technology Ltd. be liable for any direct, indirect, incidental, special,
exemplary, or consequential damaged (including, but not limited to,
procurement of substitute good or services; loss of use, data, or profits;
or business interruption) however caused and on theory of liability. Whether
in contract, strict liability or tort (including negligence or otherwise)
arising in any way out of the use of this software, even if advised of the
possibility of such damage.
Written by Adam Twiss (adam@zeus.co.uk). March 1996
Thanks to the following people for their input:
Mike Belshe (mbelshe@netscape.com)
Michael Campanella (campanella@stevms.enet.dec.com)
*/
For the expat xml parser component:
Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
and Clark Cooper
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
====================================================================

112
src/cachetools/__init__.py Normal file
View File

@@ -0,0 +1,112 @@
"""Extensible memoizing collections and decorators."""
from __future__ import absolute_import
import functools
from . import keys
from .cache import Cache
from .lfu import LFUCache
from .lru import LRUCache
from .rr import RRCache
from .ttl import TTLCache
__all__ = (
'Cache', 'LFUCache', 'LRUCache', 'RRCache', 'TTLCache',
'cached', 'cachedmethod'
)
__version__ = '3.1.0'
if hasattr(functools.update_wrapper(lambda f: f(), lambda: 42), '__wrapped__'):
_update_wrapper = functools.update_wrapper
else:
def _update_wrapper(wrapper, wrapped):
functools.update_wrapper(wrapper, wrapped)
wrapper.__wrapped__ = wrapped
return wrapper
def cached(cache, key=keys.hashkey, lock=None):
"""Decorator to wrap a function with a memoizing callable that saves
results in a cache.
"""
def decorator(func):
if cache is None:
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
elif lock is None:
def wrapper(*args, **kwargs):
k = key(*args, **kwargs)
try:
return cache[k]
except KeyError:
pass # key not found
v = func(*args, **kwargs)
try:
cache[k] = v
except ValueError:
pass # value too large
return v
else:
def wrapper(*args, **kwargs):
k = key(*args, **kwargs)
try:
with lock:
return cache[k]
except KeyError:
pass # key not found
v = func(*args, **kwargs)
try:
with lock:
cache[k] = v
except ValueError:
pass # value too large
return v
return _update_wrapper(wrapper, func)
return decorator
def cachedmethod(cache, key=keys.hashkey, lock=None):
"""Decorator to wrap a class or instance method with a memoizing
callable that saves results in a cache.
"""
def decorator(method):
if lock is None:
def wrapper(self, *args, **kwargs):
c = cache(self)
if c is None:
return method(self, *args, **kwargs)
k = key(*args, **kwargs)
try:
return c[k]
except KeyError:
pass # key not found
v = method(self, *args, **kwargs)
try:
c[k] = v
except ValueError:
pass # value too large
return v
else:
def wrapper(self, *args, **kwargs):
c = cache(self)
if c is None:
return method(self, *args, **kwargs)
k = key(*args, **kwargs)
try:
with lock(self):
return c[k]
except KeyError:
pass # key not found
v = method(self, *args, **kwargs)
try:
with lock(self):
c[k] = v
except ValueError:
pass # value too large
return v
return _update_wrapper(wrapper, method)
return decorator

52
src/cachetools/abc.py Normal file
View File

@@ -0,0 +1,52 @@
from __future__ import absolute_import
from abc import abstractmethod
try:
from collections.abc import MutableMapping
except ImportError:
from collections import MutableMapping
class DefaultMapping(MutableMapping):
__slots__ = ()
@abstractmethod
def __contains__(self, key): # pragma: nocover
return False
@abstractmethod
def __getitem__(self, key): # pragma: nocover
if hasattr(self.__class__, '__missing__'):
return self.__class__.__missing__(self, key)
else:
raise KeyError(key)
def get(self, key, default=None):
if key in self:
return self[key]
else:
return default
__marker = object()
def pop(self, key, default=__marker):
if key in self:
value = self[key]
del self[key]
elif default is self.__marker:
raise KeyError(key)
else:
value = default
return value
def setdefault(self, key, default=None):
if key in self:
value = self[key]
else:
self[key] = value = default
return value
DefaultMapping.register(dict)

91
src/cachetools/cache.py Normal file
View File

@@ -0,0 +1,91 @@
from __future__ import absolute_import
from .abc import DefaultMapping
class _DefaultSize(object):
def __getitem__(self, _):
return 1
def __setitem__(self, _, value):
assert value == 1
def pop(self, _):
return 1
class Cache(DefaultMapping):
"""Mutable mapping to serve as a simple cache or cache base class."""
__size = _DefaultSize()
def __init__(self, maxsize, getsizeof=None):
if getsizeof:
self.getsizeof = getsizeof
if self.getsizeof is not Cache.getsizeof:
self.__size = dict()
self.__data = dict()
self.__currsize = 0
self.__maxsize = maxsize
def __repr__(self):
return '%s(%r, maxsize=%r, currsize=%r)' % (
self.__class__.__name__,
list(self.__data.items()),
self.__maxsize,
self.__currsize,
)
def __getitem__(self, key):
try:
return self.__data[key]
except KeyError:
return self.__missing__(key)
def __setitem__(self, key, value):
maxsize = self.__maxsize
size = self.getsizeof(value)
if size > maxsize:
raise ValueError('value too large')
if key not in self.__data or self.__size[key] < size:
while self.__currsize + size > maxsize:
self.popitem()
if key in self.__data:
diffsize = size - self.__size[key]
else:
diffsize = size
self.__data[key] = value
self.__size[key] = size
self.__currsize += diffsize
def __delitem__(self, key):
size = self.__size.pop(key)
del self.__data[key]
self.__currsize -= size
def __contains__(self, key):
return key in self.__data
def __missing__(self, key):
raise KeyError(key)
def __iter__(self):
return iter(self.__data)
def __len__(self):
return len(self.__data)
@property
def maxsize(self):
"""The maximum size of the cache."""
return self.__maxsize
@property
def currsize(self):
"""The current size of the cache."""
return self.__currsize
@staticmethod
def getsizeof(value):
"""Return the size of a cache element's value."""
return 1

140
src/cachetools/func.py Normal file
View File

@@ -0,0 +1,140 @@
"""`functools.lru_cache` compatible memoizing function decorators."""
from __future__ import absolute_import
import collections
import functools
import random
try:
from time import monotonic as default_timer
except ImportError:
from time import time as default_timer
try:
from threading import RLock
except ImportError: # pragma: no cover
from dummy_threading import RLock
from . import keys
from .lfu import LFUCache
from .lru import LRUCache
from .rr import RRCache
from .ttl import TTLCache
__all__ = ('lfu_cache', 'lru_cache', 'rr_cache', 'ttl_cache')
_CacheInfo = collections.namedtuple('CacheInfo', [
'hits', 'misses', 'maxsize', 'currsize'
])
class _UnboundCache(dict):
maxsize = None
@property
def currsize(self):
return len(self)
class _UnboundTTLCache(TTLCache):
def __init__(self, ttl, timer):
TTLCache.__init__(self, float('inf'), ttl, timer)
@property
def maxsize(self):
return None
def _cache(cache, typed=False):
def decorator(func):
key = keys.typedkey if typed else keys.hashkey
lock = RLock()
stats = [0, 0]
def cache_info():
with lock:
hits, misses = stats
maxsize = cache.maxsize
currsize = cache.currsize
return _CacheInfo(hits, misses, maxsize, currsize)
def cache_clear():
with lock:
try:
cache.clear()
finally:
stats[:] = [0, 0]
def wrapper(*args, **kwargs):
k = key(*args, **kwargs)
with lock:
try:
v = cache[k]
stats[0] += 1
return v
except KeyError:
stats[1] += 1
v = func(*args, **kwargs)
try:
with lock:
cache[k] = v
except ValueError:
pass # value too large
return v
functools.update_wrapper(wrapper, func)
if not hasattr(wrapper, '__wrapped__'):
wrapper.__wrapped__ = func # Python 2.7
wrapper.cache_info = cache_info
wrapper.cache_clear = cache_clear
return wrapper
return decorator
def lfu_cache(maxsize=128, typed=False):
"""Decorator to wrap a function with a memoizing callable that saves
up to `maxsize` results based on a Least Frequently Used (LFU)
algorithm.
"""
if maxsize is None:
return _cache(_UnboundCache(), typed)
else:
return _cache(LFUCache(maxsize), typed)
def lru_cache(maxsize=128, typed=False):
"""Decorator to wrap a function with a memoizing callable that saves
up to `maxsize` results based on a Least Recently Used (LRU)
algorithm.
"""
if maxsize is None:
return _cache(_UnboundCache(), typed)
else:
return _cache(LRUCache(maxsize), typed)
def rr_cache(maxsize=128, choice=random.choice, typed=False):
"""Decorator to wrap a function with a memoizing callable that saves
up to `maxsize` results based on a Random Replacement (RR)
algorithm.
"""
if maxsize is None:
return _cache(_UnboundCache(), typed)
else:
return _cache(RRCache(maxsize, choice), typed)
def ttl_cache(maxsize=128, ttl=600, timer=default_timer, typed=False):
"""Decorator to wrap a function with a memoizing callable that saves
up to `maxsize` results based on a Least Recently Used (LRU)
algorithm with a per-item time-to-live (TTL) value.
"""
if maxsize is None:
return _cache(_UnboundTTLCache(ttl, timer), typed)
else:
return _cache(TTLCache(maxsize, ttl, timer), typed)

43
src/cachetools/keys.py Normal file
View File

@@ -0,0 +1,43 @@
"""Key functions for memoizing decorators."""
from __future__ import absolute_import
__all__ = ('hashkey', 'typedkey')
class _HashedTuple(tuple):
__hashvalue = None
def __hash__(self, hash=tuple.__hash__):
hashvalue = self.__hashvalue
if hashvalue is None:
self.__hashvalue = hashvalue = hash(self)
return hashvalue
def __add__(self, other, add=tuple.__add__):
return _HashedTuple(add(self, other))
def __radd__(self, other, add=tuple.__add__):
return _HashedTuple(add(other, self))
_kwmark = (object(),)
def hashkey(*args, **kwargs):
"""Return a cache key for the specified hashable arguments."""
if kwargs:
return _HashedTuple(args + sum(sorted(kwargs.items()), _kwmark))
else:
return _HashedTuple(args)
def typedkey(*args, **kwargs):
"""Return a typed cache key for the specified hashable arguments."""
key = hashkey(*args, **kwargs)
key += tuple(type(v) for v in args)
key += tuple(type(v) for _, v in sorted(kwargs.items()))
return key

35
src/cachetools/lfu.py Normal file
View File

@@ -0,0 +1,35 @@
from __future__ import absolute_import
import collections
from .cache import Cache
class LFUCache(Cache):
"""Least Frequently Used (LFU) cache implementation."""
def __init__(self, maxsize, getsizeof=None):
Cache.__init__(self, maxsize, getsizeof)
self.__counter = collections.Counter()
def __getitem__(self, key, cache_getitem=Cache.__getitem__):
value = cache_getitem(self, key)
self.__counter[key] -= 1
return value
def __setitem__(self, key, value, cache_setitem=Cache.__setitem__):
cache_setitem(self, key, value)
self.__counter[key] -= 1
def __delitem__(self, key, cache_delitem=Cache.__delitem__):
cache_delitem(self, key)
del self.__counter[key]
def popitem(self):
"""Remove and return the `(key, value)` pair least frequently used."""
try:
(key, _), = self.__counter.most_common(1)
except ValueError:
raise KeyError('%s is empty' % self.__class__.__name__)
else:
return (key, self.pop(key))

48
src/cachetools/lru.py Normal file
View File

@@ -0,0 +1,48 @@
from __future__ import absolute_import
import collections
from .cache import Cache
class LRUCache(Cache):
"""Least Recently Used (LRU) cache implementation."""
def __init__(self, maxsize, getsizeof=None):
Cache.__init__(self, maxsize, getsizeof)
self.__order = collections.OrderedDict()
def __getitem__(self, key, cache_getitem=Cache.__getitem__):
value = cache_getitem(self, key)
self.__update(key)
return value
def __setitem__(self, key, value, cache_setitem=Cache.__setitem__):
cache_setitem(self, key, value)
self.__update(key)
def __delitem__(self, key, cache_delitem=Cache.__delitem__):
cache_delitem(self, key)
del self.__order[key]
def popitem(self):
"""Remove and return the `(key, value)` pair least recently used."""
try:
key = next(iter(self.__order))
except StopIteration:
raise KeyError('%s is empty' % self.__class__.__name__)
else:
return (key, self.pop(key))
if hasattr(collections.OrderedDict, 'move_to_end'):
def __update(self, key):
try:
self.__order.move_to_end(key)
except KeyError:
self.__order[key] = None
else:
def __update(self, key):
try:
self.__order[key] = self.__order.pop(key)
except KeyError:
self.__order[key] = None

36
src/cachetools/rr.py Normal file
View File

@@ -0,0 +1,36 @@
from __future__ import absolute_import
import random
from .cache import Cache
# random.choice cannot be pickled in Python 2.7
def _choice(seq):
return random.choice(seq)
class RRCache(Cache):
"""Random Replacement (RR) cache implementation."""
def __init__(self, maxsize, choice=random.choice, getsizeof=None):
Cache.__init__(self, maxsize, getsizeof)
# TODO: use None as default, assing to self.choice directly?
if choice is random.choice:
self.__choice = _choice
else:
self.__choice = choice
@property
def choice(self):
"""The `choice` function used by the cache."""
return self.__choice
def popitem(self):
"""Remove and return a random `(key, value)` pair."""
try:
key = self.__choice(list(self))
except IndexError:
raise KeyError('%s is empty' % self.__class__.__name__)
else:
return (key, self.pop(key))

220
src/cachetools/ttl.py Normal file
View File

@@ -0,0 +1,220 @@
from __future__ import absolute_import
import collections
try:
from time import monotonic as default_timer
except ImportError:
from time import time as default_timer
from .cache import Cache
class _Link(object):
__slots__ = ('key', 'expire', 'next', 'prev')
def __init__(self, key=None, expire=None):
self.key = key
self.expire = expire
def __reduce__(self):
return _Link, (self.key, self.expire)
def unlink(self):
next = self.next
prev = self.prev
prev.next = next
next.prev = prev
class _Timer(object):
def __init__(self, timer):
self.__timer = timer
self.__nesting = 0
def __call__(self):
if self.__nesting == 0:
return self.__timer()
else:
return self.__time
def __enter__(self):
if self.__nesting == 0:
self.__time = time = self.__timer()
else:
time = self.__time
self.__nesting += 1
return time
def __exit__(self, *exc):
self.__nesting -= 1
def __reduce__(self):
return _Timer, (self.__timer,)
def __getattr__(self, name):
return getattr(self.__timer, name)
class TTLCache(Cache):
"""LRU Cache implementation with per-item time-to-live (TTL) value."""
def __init__(self, maxsize, ttl, timer=default_timer, getsizeof=None):
Cache.__init__(self, maxsize, getsizeof)
self.__root = root = _Link()
root.prev = root.next = root
self.__links = collections.OrderedDict()
self.__timer = _Timer(timer)
self.__ttl = ttl
def __contains__(self, key):
try:
link = self.__links[key] # no reordering
except KeyError:
return False
else:
return not (link.expire < self.__timer())
def __getitem__(self, key, cache_getitem=Cache.__getitem__):
try:
link = self.__getlink(key)
except KeyError:
expired = False
else:
expired = link.expire < self.__timer()
if expired:
return self.__missing__(key)
else:
return cache_getitem(self, key)
def __setitem__(self, key, value, cache_setitem=Cache.__setitem__):
with self.__timer as time:
self.expire(time)
cache_setitem(self, key, value)
try:
link = self.__getlink(key)
except KeyError:
self.__links[key] = link = _Link(key)
else:
link.unlink()
link.expire = time + self.__ttl
link.next = root = self.__root
link.prev = prev = root.prev
prev.next = root.prev = link
def __delitem__(self, key, cache_delitem=Cache.__delitem__):
cache_delitem(self, key)
link = self.__links.pop(key)
link.unlink()
if link.expire < self.__timer():
raise KeyError(key)
def __iter__(self):
root = self.__root
curr = root.next
while curr is not root:
# "freeze" time for iterator access
with self.__timer as time:
if not (curr.expire < time):
yield curr.key
curr = curr.next
def __len__(self):
root = self.__root
curr = root.next
time = self.__timer()
count = len(self.__links)
while curr is not root and curr.expire < time:
count -= 1
curr = curr.next
return count
def __setstate__(self, state):
self.__dict__.update(state)
root = self.__root
root.prev = root.next = root
for link in sorted(self.__links.values(), key=lambda obj: obj.expire):
link.next = root
link.prev = prev = root.prev
prev.next = root.prev = link
self.expire(self.__timer())
def __repr__(self, cache_repr=Cache.__repr__):
with self.__timer as time:
self.expire(time)
return cache_repr(self)
@property
def currsize(self):
with self.__timer as time:
self.expire(time)
return super(TTLCache, self).currsize
@property
def timer(self):
"""The timer function used by the cache."""
return self.__timer
@property
def ttl(self):
"""The time-to-live value of the cache's items."""
return self.__ttl
def expire(self, time=None):
"""Remove expired items from the cache."""
if time is None:
time = self.__timer()
root = self.__root
curr = root.next
links = self.__links
cache_delitem = Cache.__delitem__
while curr is not root and curr.expire < time:
cache_delitem(self, curr.key)
del links[curr.key]
next = curr.next
curr.unlink()
curr = next
def clear(self):
with self.__timer as time:
self.expire(time)
Cache.clear(self)
def get(self, *args, **kwargs):
with self.__timer:
return Cache.get(self, *args, **kwargs)
def pop(self, *args, **kwargs):
with self.__timer:
return Cache.pop(self, *args, **kwargs)
def setdefault(self, *args, **kwargs):
with self.__timer:
return Cache.setdefault(self, *args, **kwargs)
def popitem(self):
"""Remove and return the `(key, value)` pair least recently used that
has not already expired.
"""
with self.__timer as time:
self.expire(time)
try:
key = next(iter(self.__links))
except StopIteration:
raise KeyError('%s is empty' % self.__class__.__name__)
else:
return (key, self.pop(key))
if hasattr(collections.OrderedDict, 'move_to_end'):
def __getlink(self, key):
value = self.__links[key]
self.__links.move_to_end(key)
return value
else:
def __getlink(self, key):
value = self.__links.pop(key)
self.__links[key] = value
return value

486
src/cloudprint-v2.json Normal file
View File

@@ -0,0 +1,486 @@
{
"kind": "discovery#restDescription",
"discoveryVersion": "v1",
"id": "cloudprint:v2",
"name": "cloudprint",
"version": "v2",
"revision": "20150605",
"title": "Cloud Print API",
"description": "Lets you access Cloud Print Printers",
"ownerDomain": "google.com",
"ownerName": "Google",
"icons": {
"x16": "http://www.google.com/images/icons/product/search-16.gif",
"x32": "http://www.google.com/images/icons/product/search-32.gif"
},
"documentationLink": "https://developers.google.com/cloud-print",
"protocol": "rest",
"baseUrl": "https://www.google.com/",
"basePath": "/cloudprint/",
"rootUrl": "https://www.google.com/",
"servicePath": "/cloudprint/",
"parameters": {
"prettyPrint": {
"type": "boolean",
"description": "Returns response with indentations and line breaks.",
"default": "true",
"location": "query"
}
},
"auth": {
"oauth2": {
"scopes": {
"https://www.googleapis.com/auth/cloudprint": {
"description": "Manage Cloud Print"
}
}
}
},
"schemas": {
"Job": {
"id": "Job",
"type": "object",
"description": "Job Object",
"properties": {
"title": {
"type": "string",
"description": "Job Title"
},
"id": {
"type": "string",
"description": "Unique ID"
}
}
},
"Jobs": {
"id": "Jobs",
"type": "object",
"description": "List of Jobs.",
"properties": {
"jobs": {
"type": "array",
"description": "List of job objects.",
"items": {
"$ref": "Job"
}
}
}
},
"Printer": {
"id": "Printer",
"type": "object",
"description": "Printer Object",
"properties": {
"displayName": {
"type": "string",
"description": "Display Name"
},
"id": {
"type": "string",
"description": "Unique ID"
}
}
},
"Printers": {
"id": "Printers",
"type": "object",
"description": "List of Printers.",
"properties": {
"printers": {
"type": "array",
"description": "List of printer objects.",
"items": {
"$ref": "Printer"
}
}
}
}
},
"resources": {
"jobs": {
"methods": {
"delete": {
"id": "cloudprint.jobs.delete",
"path": "deletejob",
"httpMethod": "GET",
"parameters": {
"jobid": {
"type": "string",
"location": "query",
"required": "true"
}
}
},
"fetch": {
"id": "cloudprint.jobs.fetch",
"path": "fetch",
"httpMethod": "GET",
"parameters": {
"printerid": {
"type": "string",
"required": "true",
"location": "query"
}
},
"response": {
"$ref": "Jobs"
}
},
"getticket": {
"id": "cloudprint.jobs.getticket",
"path": "ticket",
"httpMethod": "GET",
"parameters": {
"jobid": {
"type": "string",
"required": "true",
"location": "query"
},
"use_cjt": {
"type": "boolean",
"required": "true",
"location": "query"
}
}
},
"list": {
"id": "cloudprint.jobs.list",
"path": "jobs",
"httpMethod": "GET",
"parameters": {
"printerid": {
"type": "string",
"location": "query"
},
"owner": {
"type": "string",
"location": "query"
},
"status": {
"type": "string",
"location": "query"
},
"q": {
"type": "string",
"location": "query"
},
"offset": {
"type": "string",
"location": "query"
},
"limit": {
"type": "string",
"location": "query"
},
"sortorder": {
"type": "string",
"location": "query"
}
},
"response": {
"$ref": "Jobs"
}
},
"update": {
"id": "cloudprint.jobs.update",
"path": "control",
"httpMethod": "GET",
"parameters": {
"jobid": {
"type": "string",
"required": "true",
"location": "query"
},
"semantic_state_diff": {
"type": "string",
"required": "true",
"location": "query"
}
},
"response": {
"$ref": "Jobs"
}
},
"resubmit": {
"id": "cloudprint.jobs.resubmit",
"path": "resubmit",
"httpMethod": "POST",
"description": "resubmit a job to new printer.",
"parameters": {
"printerid": {
"type": "string",
"required": "true",
"location": "query"
},
"jobid": {
"type": "string",
"required": "true",
"location": "query"
},
"ticket": {
"type": "string",
"location": "query"
}
},
"response": {
"$ref": "Job"
}
},
"submit": {
"id": "cloudprint.jobs.submit",
"path": "submit",
"httpMethod": "POST",
"description": "Send a print job to cloud print.",
"request": {
"printerid": {
"type": "string",
"required": "true",
"location": "query"
},
"title": {
"type": "string",
"location": "query"
},
"ticket": {
"type": "string",
"location": "query"
},
"content": {
"type": "string",
"location": "query"
},
"contentType": {
"type": "string",
"location": "query"
},
"tag": {
"type": "string",
"location": "query"
}
},
"response": {
"$ref": "Job"
}
}
}
},
"printers": {
"methods": {
"get": {
"id": "cloudprint.printers.get",
"path": "printer",
"httpMethod": "GET",
"parameters": {
"printerid": {
"type": "string",
"required": "true",
"location": "query"
},
"extra_fields": {
"type": "string",
"location": "query"
}
},
"response": {
"$ref": "Printer"
}
},
"list": {
"id": "cloudprint.printers.list",
"path": "search",
"httpMethod": "GET",
"description": "List all printers",
"parameters": {
"q": {
"type": "string",
"description": "Query list of printers",
"location": "query"
},
"type": {
"type": "string",
"description": "limit results to printers of type",
"location": "query"
},
"connection_status": {
"type": "string",
"description": "limit results to printers with this status",
"location": "query"
},
"extra_fields": {
"type": "string",
"description": "include extra fields",
"location": "query"
}
},
"response": {
"$ref": "Printers"
}
},
"share": {
"id": "cloudprint.printers.share",
"path": "share",
"httpMethod": "GET",
"description": "Share printer with user, group or domain",
"parameters": {
"printerid": {
"type": "string",
"required": "true",
"location": "query"
},
"scope": {
"type": "string",
"location": "query"
},
"role": {
"type": "string",
"location": "query"
},
"type": {
"type": "string",
"location": "query"
},
"skip_notification": {
"type": "boolean",
"location": "query"
},
"public": {
"type": "boolean",
"location": "query"
}
}
},
"unshare": {
"id": "cloudprint.printers.unshare",
"path": "unshare",
"httpMethod": "GET",
"description": "unshare printer with user, group or domain",
"parameters": {
"printerid": {
"type": "string",
"required": "true",
"location": "query"
},
"scope": {
"type": "string",
"location": "query"
},
"public": {
"type": "string",
"location": "query"
}
}
},
"delete": {
"id": "cloudprint.printers.delete",
"path": "delete",
"httpMethod": "GET",
"description": "delete a printer",
"parameters": {
"printerid": {
"type": "string",
"required": "true",
"location": "query"
}
}
},
"update": {
"id": "cloudprint.printers.update",
"path": "update",
"httpMethod": "GET",
"description": "update a printer",
"parameters": {
"isTosAccepted": {
"type": "boolean",
"location": "query"
},
"gcpVersion": {
"type": "string",
"location": "query"
},
"setupUrl": {
"type": "string",
"location": "query"
},
"supportUrl": {
"type": "string",
"location": "query"
},
"firmware": {
"type": "string",
"location": "query"
},
"currentQuota": {
"type": "string",
"location": "query"
},
"type": {
"type": "string",
"location": "query"
},
"public": {
"type": "boolean",
"location": "query"
},
"status": {
"type": "string",
"location": "query"
},
"proxy": {
"type": "string",
"location": "query"
},
"manufacturer": {
"type": "string",
"location": "query"
},
"defaultDisplayName": {
"type": "string",
"location": "query"
},
"displayName": {
"type": "string",
"location": "query"
},
"name": {
"type": "string",
"location": "query"
},
"uuid": {
"type": "string",
"location": "query"
},
"updateUrl": {
"type": "string",
"location": "query"
},
"ownerId": {
"type": "string",
"location": "query"
},
"model": {
"type": "string",
"location": "query"
},
"description": {
"type": "string",
"location": "query"
},
"printerid": {
"type": "string",
"required": "true",
"location": "query"
},
"quotaEnabled": {
"type": "boolean",
"location": "query"
},
"dailyQuota": {
"type": "string",
"location": "query"
}
}
}
}
}
}
}

56
src/dns/__init__.py Normal file
View File

@@ -0,0 +1,56 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""dnspython DNS toolkit"""
__all__ = [
'dnssec',
'e164',
'edns',
'entropy',
'exception',
'flags',
'hash',
'inet',
'ipv4',
'ipv6',
'message',
'name',
'namedict',
'node',
'opcode',
'query',
'rcode',
'rdata',
'rdataclass',
'rdataset',
'rdatatype',
'renderer',
'resolver',
'reversename',
'rrset',
'set',
'tokenizer',
'tsig',
'tsigkeyring',
'ttl',
'rdtypes',
'update',
'version',
'wiredata',
'zone',
]

59
src/dns/_compat.py Normal file
View File

@@ -0,0 +1,59 @@
import sys
import decimal
from decimal import Context
PY3 = sys.version_info[0] == 3
PY2 = sys.version_info[0] == 2
if PY3:
long = int
xrange = range
else:
long = long # pylint: disable=long-builtin
xrange = xrange # pylint: disable=xrange-builtin
# unicode / binary types
if PY3:
text_type = str
binary_type = bytes
string_types = (str,)
unichr = chr
def maybe_decode(x):
return x.decode()
def maybe_encode(x):
return x.encode()
def maybe_chr(x):
return x
def maybe_ord(x):
return x
else:
text_type = unicode # pylint: disable=unicode-builtin, undefined-variable
binary_type = str
string_types = (
basestring, # pylint: disable=basestring-builtin, undefined-variable
)
unichr = unichr # pylint: disable=unichr-builtin
def maybe_decode(x):
return x
def maybe_encode(x):
return x
def maybe_chr(x):
return chr(x)
def maybe_ord(x):
return ord(x)
def round_py2_compat(what):
"""
Python 2 and Python 3 use different rounding strategies in round(). This
function ensures that results are python2/3 compatible and backward
compatible with previous py2 releases
:param what: float
:return: rounded long
"""
d = Context(
prec=len(str(long(what))), # round to integer with max precision
rounding=decimal.ROUND_HALF_UP
).create_decimal(str(what)) # str(): python 2.6 compat
return long(d)

519
src/dns/dnssec.py Normal file
View File

@@ -0,0 +1,519 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""Common DNSSEC-related functions and constants."""
from io import BytesIO
import struct
import time
import dns.exception
import dns.name
import dns.node
import dns.rdataset
import dns.rdata
import dns.rdatatype
import dns.rdataclass
from ._compat import string_types
class UnsupportedAlgorithm(dns.exception.DNSException):
"""The DNSSEC algorithm is not supported."""
class ValidationFailure(dns.exception.DNSException):
"""The DNSSEC signature is invalid."""
#: RSAMD5
RSAMD5 = 1
#: DH
DH = 2
#: DSA
DSA = 3
#: ECC
ECC = 4
#: RSASHA1
RSASHA1 = 5
#: DSANSEC3SHA1
DSANSEC3SHA1 = 6
#: RSASHA1NSEC3SHA1
RSASHA1NSEC3SHA1 = 7
#: RSASHA256
RSASHA256 = 8
#: RSASHA512
RSASHA512 = 10
#: ECDSAP256SHA256
ECDSAP256SHA256 = 13
#: ECDSAP384SHA384
ECDSAP384SHA384 = 14
#: INDIRECT
INDIRECT = 252
#: PRIVATEDNS
PRIVATEDNS = 253
#: PRIVATEOID
PRIVATEOID = 254
_algorithm_by_text = {
'RSAMD5': RSAMD5,
'DH': DH,
'DSA': DSA,
'ECC': ECC,
'RSASHA1': RSASHA1,
'DSANSEC3SHA1': DSANSEC3SHA1,
'RSASHA1NSEC3SHA1': RSASHA1NSEC3SHA1,
'RSASHA256': RSASHA256,
'RSASHA512': RSASHA512,
'INDIRECT': INDIRECT,
'ECDSAP256SHA256': ECDSAP256SHA256,
'ECDSAP384SHA384': ECDSAP384SHA384,
'PRIVATEDNS': PRIVATEDNS,
'PRIVATEOID': PRIVATEOID,
}
# We construct the inverse mapping programmatically to ensure that we
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
# would cause the mapping not to be true inverse.
_algorithm_by_value = {y: x for x, y in _algorithm_by_text.items()}
def algorithm_from_text(text):
"""Convert text into a DNSSEC algorithm value.
Returns an ``int``.
"""
value = _algorithm_by_text.get(text.upper())
if value is None:
value = int(text)
return value
def algorithm_to_text(value):
"""Convert a DNSSEC algorithm value to text
Returns a ``str``.
"""
text = _algorithm_by_value.get(value)
if text is None:
text = str(value)
return text
def _to_rdata(record, origin):
s = BytesIO()
record.to_wire(s, origin=origin)
return s.getvalue()
def key_id(key, origin=None):
"""Return the key id (a 16-bit number) for the specified key.
Note the *origin* parameter of this function is historical and
is not needed.
Returns an ``int`` between 0 and 65535.
"""
rdata = _to_rdata(key, origin)
rdata = bytearray(rdata)
if key.algorithm == RSAMD5:
return (rdata[-3] << 8) + rdata[-2]
else:
total = 0
for i in range(len(rdata) // 2):
total += (rdata[2 * i] << 8) + \
rdata[2 * i + 1]
if len(rdata) % 2 != 0:
total += rdata[len(rdata) - 1] << 8
total += ((total >> 16) & 0xffff)
return total & 0xffff
def make_ds(name, key, algorithm, origin=None):
"""Create a DS record for a DNSSEC key.
*name* is the owner name of the DS record.
*key* is a ``dns.rdtypes.ANY.DNSKEY``.
*algorithm* is a string describing which hash algorithm to use. The
currently supported hashes are "SHA1" and "SHA256". Case does not
matter for these strings.
*origin* is a ``dns.name.Name`` and will be used as the origin
if *key* is a relative name.
Returns a ``dns.rdtypes.ANY.DS``.
"""
if algorithm.upper() == 'SHA1':
dsalg = 1
hash = SHA1.new()
elif algorithm.upper() == 'SHA256':
dsalg = 2
hash = SHA256.new()
else:
raise UnsupportedAlgorithm('unsupported algorithm "%s"' % algorithm)
if isinstance(name, string_types):
name = dns.name.from_text(name, origin)
hash.update(name.canonicalize().to_wire())
hash.update(_to_rdata(key, origin))
digest = hash.digest()
dsrdata = struct.pack("!HBB", key_id(key), key.algorithm, dsalg) + digest
return dns.rdata.from_wire(dns.rdataclass.IN, dns.rdatatype.DS, dsrdata, 0,
len(dsrdata))
def _find_candidate_keys(keys, rrsig):
candidate_keys = []
value = keys.get(rrsig.signer)
if value is None:
return None
if isinstance(value, dns.node.Node):
try:
rdataset = value.find_rdataset(dns.rdataclass.IN,
dns.rdatatype.DNSKEY)
except KeyError:
return None
else:
rdataset = value
for rdata in rdataset:
if rdata.algorithm == rrsig.algorithm and \
key_id(rdata) == rrsig.key_tag:
candidate_keys.append(rdata)
return candidate_keys
def _is_rsa(algorithm):
return algorithm in (RSAMD5, RSASHA1,
RSASHA1NSEC3SHA1, RSASHA256,
RSASHA512)
def _is_dsa(algorithm):
return algorithm in (DSA, DSANSEC3SHA1)
def _is_ecdsa(algorithm):
return _have_ecdsa and (algorithm in (ECDSAP256SHA256, ECDSAP384SHA384))
def _is_md5(algorithm):
return algorithm == RSAMD5
def _is_sha1(algorithm):
return algorithm in (DSA, RSASHA1,
DSANSEC3SHA1, RSASHA1NSEC3SHA1)
def _is_sha256(algorithm):
return algorithm in (RSASHA256, ECDSAP256SHA256)
def _is_sha384(algorithm):
return algorithm == ECDSAP384SHA384
def _is_sha512(algorithm):
return algorithm == RSASHA512
def _make_hash(algorithm):
if _is_md5(algorithm):
return MD5.new()
if _is_sha1(algorithm):
return SHA1.new()
if _is_sha256(algorithm):
return SHA256.new()
if _is_sha384(algorithm):
return SHA384.new()
if _is_sha512(algorithm):
return SHA512.new()
raise ValidationFailure('unknown hash for algorithm %u' % algorithm)
def _make_algorithm_id(algorithm):
if _is_md5(algorithm):
oid = [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05]
elif _is_sha1(algorithm):
oid = [0x2b, 0x0e, 0x03, 0x02, 0x1a]
elif _is_sha256(algorithm):
oid = [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01]
elif _is_sha512(algorithm):
oid = [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03]
else:
raise ValidationFailure('unknown algorithm %u' % algorithm)
olen = len(oid)
dlen = _make_hash(algorithm).digest_size
idbytes = [0x30] + [8 + olen + dlen] + \
[0x30, olen + 4] + [0x06, olen] + oid + \
[0x05, 0x00] + [0x04, dlen]
return struct.pack('!%dB' % len(idbytes), *idbytes)
def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
"""Validate an RRset against a single signature rdata
The owner name of *rrsig* is assumed to be the same as the owner name
of *rrset*.
*rrset* is the RRset to validate. It can be a ``dns.rrset.RRset`` or
a ``(dns.name.Name, dns.rdataset.Rdataset)`` tuple.
*rrsig* is a ``dns.rdata.Rdata``, the signature to validate.
*keys* is the key dictionary, used to find the DNSKEY associated with
a given name. The dictionary is keyed by a ``dns.name.Name``, and has
``dns.node.Node`` or ``dns.rdataset.Rdataset`` values.
*origin* is a ``dns.name.Name``, the origin to use for relative names.
*now* is an ``int``, the time to use when validating the signatures,
in seconds since the UNIX epoch. The default is the current time.
"""
if isinstance(origin, string_types):
origin = dns.name.from_text(origin, dns.name.root)
candidate_keys = _find_candidate_keys(keys, rrsig)
if candidate_keys is None:
raise ValidationFailure('unknown key')
for candidate_key in candidate_keys:
# For convenience, allow the rrset to be specified as a (name,
# rdataset) tuple as well as a proper rrset
if isinstance(rrset, tuple):
rrname = rrset[0]
rdataset = rrset[1]
else:
rrname = rrset.name
rdataset = rrset
if now is None:
now = time.time()
if rrsig.expiration < now:
raise ValidationFailure('expired')
if rrsig.inception > now:
raise ValidationFailure('not yet valid')
hash = _make_hash(rrsig.algorithm)
if _is_rsa(rrsig.algorithm):
keyptr = candidate_key.key
(bytes_,) = struct.unpack('!B', keyptr[0:1])
keyptr = keyptr[1:]
if bytes_ == 0:
(bytes_,) = struct.unpack('!H', keyptr[0:2])
keyptr = keyptr[2:]
rsa_e = keyptr[0:bytes_]
rsa_n = keyptr[bytes_:]
try:
pubkey = CryptoRSA.construct(
(number.bytes_to_long(rsa_n),
number.bytes_to_long(rsa_e)))
except ValueError:
raise ValidationFailure('invalid public key')
sig = rrsig.signature
elif _is_dsa(rrsig.algorithm):
keyptr = candidate_key.key
(t,) = struct.unpack('!B', keyptr[0:1])
keyptr = keyptr[1:]
octets = 64 + t * 8
dsa_q = keyptr[0:20]
keyptr = keyptr[20:]
dsa_p = keyptr[0:octets]
keyptr = keyptr[octets:]
dsa_g = keyptr[0:octets]
keyptr = keyptr[octets:]
dsa_y = keyptr[0:octets]
pubkey = CryptoDSA.construct(
(number.bytes_to_long(dsa_y),
number.bytes_to_long(dsa_g),
number.bytes_to_long(dsa_p),
number.bytes_to_long(dsa_q)))
sig = rrsig.signature[1:]
elif _is_ecdsa(rrsig.algorithm):
# use ecdsa for NIST-384p -- not currently supported by pycryptodome
keyptr = candidate_key.key
if rrsig.algorithm == ECDSAP256SHA256:
curve = ecdsa.curves.NIST256p
key_len = 32
elif rrsig.algorithm == ECDSAP384SHA384:
curve = ecdsa.curves.NIST384p
key_len = 48
x = number.bytes_to_long(keyptr[0:key_len])
y = number.bytes_to_long(keyptr[key_len:key_len * 2])
if not ecdsa.ecdsa.point_is_valid(curve.generator, x, y):
raise ValidationFailure('invalid ECDSA key')
point = ecdsa.ellipticcurve.Point(curve.curve, x, y, curve.order)
verifying_key = ecdsa.keys.VerifyingKey.from_public_point(point,
curve)
pubkey = ECKeyWrapper(verifying_key, key_len)
r = rrsig.signature[:key_len]
s = rrsig.signature[key_len:]
sig = ecdsa.ecdsa.Signature(number.bytes_to_long(r),
number.bytes_to_long(s))
else:
raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm)
hash.update(_to_rdata(rrsig, origin)[:18])
hash.update(rrsig.signer.to_digestable(origin))
if rrsig.labels < len(rrname) - 1:
suffix = rrname.split(rrsig.labels + 1)[1]
rrname = dns.name.from_text('*', suffix)
rrnamebuf = rrname.to_digestable(origin)
rrfixed = struct.pack('!HHI', rdataset.rdtype, rdataset.rdclass,
rrsig.original_ttl)
rrlist = sorted(rdataset)
for rr in rrlist:
hash.update(rrnamebuf)
hash.update(rrfixed)
rrdata = rr.to_digestable(origin)
rrlen = struct.pack('!H', len(rrdata))
hash.update(rrlen)
hash.update(rrdata)
try:
if _is_rsa(rrsig.algorithm):
verifier = pkcs1_15.new(pubkey)
# will raise ValueError if verify fails:
verifier.verify(hash, sig)
elif _is_dsa(rrsig.algorithm):
verifier = DSS.new(pubkey, 'fips-186-3')
verifier.verify(hash, sig)
elif _is_ecdsa(rrsig.algorithm):
digest = hash.digest()
if not pubkey.verify(digest, sig):
raise ValueError
else:
# Raise here for code clarity; this won't actually ever happen
# since if the algorithm is really unknown we'd already have
# raised an exception above
raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm)
# If we got here, we successfully verified so we can return without error
return
except ValueError:
# this happens on an individual validation failure
continue
# nothing verified -- raise failure:
raise ValidationFailure('verify failure')
def _validate(rrset, rrsigset, keys, origin=None, now=None):
"""Validate an RRset.
*rrset* is the RRset to validate. It can be a ``dns.rrset.RRset`` or
a ``(dns.name.Name, dns.rdataset.Rdataset)`` tuple.
*rrsigset* is the signature RRset to be validated. It can be a
``dns.rrset.RRset`` or a ``(dns.name.Name, dns.rdataset.Rdataset)`` tuple.
*keys* is the key dictionary, used to find the DNSKEY associated with
a given name. The dictionary is keyed by a ``dns.name.Name``, and has
``dns.node.Node`` or ``dns.rdataset.Rdataset`` values.
*origin* is a ``dns.name.Name``, the origin to use for relative names.
*now* is an ``int``, the time to use when validating the signatures,
in seconds since the UNIX epoch. The default is the current time.
"""
if isinstance(origin, string_types):
origin = dns.name.from_text(origin, dns.name.root)
if isinstance(rrset, tuple):
rrname = rrset[0]
else:
rrname = rrset.name
if isinstance(rrsigset, tuple):
rrsigname = rrsigset[0]
rrsigrdataset = rrsigset[1]
else:
rrsigname = rrsigset.name
rrsigrdataset = rrsigset
rrname = rrname.choose_relativity(origin)
rrsigname = rrsigname.choose_relativity(origin)
if rrname != rrsigname:
raise ValidationFailure("owner names do not match")
for rrsig in rrsigrdataset:
try:
_validate_rrsig(rrset, rrsig, keys, origin, now)
return
except ValidationFailure:
pass
raise ValidationFailure("no RRSIGs validated")
def _need_pycrypto(*args, **kwargs):
raise NotImplementedError("DNSSEC validation requires pycryptodome/pycryptodomex")
try:
try:
# test we're using pycryptodome, not pycrypto (which misses SHA1 for example)
from Crypto.Hash import MD5, SHA1, SHA256, SHA384, SHA512
from Crypto.PublicKey import RSA as CryptoRSA, DSA as CryptoDSA
from Crypto.Signature import pkcs1_15, DSS
from Crypto.Util import number
except ImportError:
from Cryptodome.Hash import MD5, SHA1, SHA256, SHA384, SHA512
from Cryptodome.PublicKey import RSA as CryptoRSA, DSA as CryptoDSA
from Cryptodome.Signature import pkcs1_15, DSS
from Cryptodome.Util import number
except ImportError:
validate = _need_pycrypto
validate_rrsig = _need_pycrypto
_have_pycrypto = False
_have_ecdsa = False
else:
validate = _validate
validate_rrsig = _validate_rrsig
_have_pycrypto = True
try:
import ecdsa
import ecdsa.ecdsa
import ecdsa.ellipticcurve
import ecdsa.keys
except ImportError:
_have_ecdsa = False
else:
_have_ecdsa = True
class ECKeyWrapper(object):
def __init__(self, key, key_len):
self.key = key
self.key_len = key_len
def verify(self, digest, sig):
diglong = number.bytes_to_long(digest)
return self.key.pubkey.verifies(diglong, sig)

19
src/dns/dnssec.pyi Normal file
View File

@@ -0,0 +1,19 @@
from typing import Union, Dict, Tuple, Optional
from . import rdataset, rrset, exception, name, rdtypes, rdata, node
import dns.rdtypes.ANY.DS as DS
import dns.rdtypes.ANY.DNSKEY as DNSKEY
_have_ecdsa : bool
_have_pycrypto : bool
def validate_rrsig(rrset : Union[Tuple[name.Name, rdataset.Rdataset], rrset.RRset], rrsig : rdata.Rdata, keys : Dict[name.Name, Union[node.Node, rdataset.Rdataset]], origin : Optional[name.Name] = None, now : Optional[int] = None) -> None:
...
def validate(rrset: Union[Tuple[name.Name, rdataset.Rdataset], rrset.RRset], rrsigset : Union[Tuple[name.Name, rdataset.Rdataset], rrset.RRset], keys : Dict[name.Name, Union[node.Node, rdataset.Rdataset]], origin=None, now=None) -> None:
...
class ValidationFailure(exception.DNSException):
...
def make_ds(name : name.Name, key : DNSKEY.DNSKEY, algorithm : str, origin : Optional[name.Name] = None) -> DS.DS:
...

105
src/dns/e164.py Normal file
View File

@@ -0,0 +1,105 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2006-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""DNS E.164 helpers."""
import dns.exception
import dns.name
import dns.resolver
from ._compat import string_types, maybe_decode
#: The public E.164 domain.
public_enum_domain = dns.name.from_text('e164.arpa.')
def from_e164(text, origin=public_enum_domain):
"""Convert an E.164 number in textual form into a Name object whose
value is the ENUM domain name for that number.
Non-digits in the text are ignored, i.e. "16505551212",
"+1.650.555.1212" and "1 (650) 555-1212" are all the same.
*text*, a ``text``, is an E.164 number in textual form.
*origin*, a ``dns.name.Name``, the domain in which the number
should be constructed. The default is ``e164.arpa.``.
Returns a ``dns.name.Name``.
"""
parts = [d for d in text if d.isdigit()]
parts.reverse()
return dns.name.from_text('.'.join(parts), origin=origin)
def to_e164(name, origin=public_enum_domain, want_plus_prefix=True):
"""Convert an ENUM domain name into an E.164 number.
Note that dnspython does not have any information about preferred
number formats within national numbering plans, so all numbers are
emitted as a simple string of digits, prefixed by a '+' (unless
*want_plus_prefix* is ``False``).
*name* is a ``dns.name.Name``, the ENUM domain name.
*origin* is a ``dns.name.Name``, a domain containing the ENUM
domain name. The name is relativized to this domain before being
converted to text. If ``None``, no relativization is done.
*want_plus_prefix* is a ``bool``. If True, add a '+' to the beginning of
the returned number.
Returns a ``text``.
"""
if origin is not None:
name = name.relativize(origin)
dlabels = [d for d in name.labels if d.isdigit() and len(d) == 1]
if len(dlabels) != len(name.labels):
raise dns.exception.SyntaxError('non-digit labels in ENUM domain name')
dlabels.reverse()
text = b''.join(dlabels)
if want_plus_prefix:
text = b'+' + text
return maybe_decode(text)
def query(number, domains, resolver=None):
"""Look for NAPTR RRs for the specified number in the specified domains.
e.g. lookup('16505551212', ['e164.dnspython.org.', 'e164.arpa.'])
*number*, a ``text`` is the number to look for.
*domains* is an iterable containing ``dns.name.Name`` values.
*resolver*, a ``dns.resolver.Resolver``, is the resolver to use. If
``None``, the default resolver is used.
"""
if resolver is None:
resolver = dns.resolver.get_default_resolver()
e_nx = dns.resolver.NXDOMAIN()
for domain in domains:
if isinstance(domain, string_types):
domain = dns.name.from_text(domain)
qname = dns.e164.from_e164(number, domain)
try:
return resolver.query(qname, 'NAPTR')
except dns.resolver.NXDOMAIN as e:
e_nx += e
raise e_nx

10
src/dns/e164.pyi Normal file
View File

@@ -0,0 +1,10 @@
from typing import Optional, Iterable
from . import name, resolver
def from_e164(text : str, origin=name.Name(".")) -> name.Name:
...
def to_e164(name : name.Name, origin : Optional[name.Name] = None, want_plus_prefix=True) -> str:
...
def query(number : str, domains : Iterable[str], resolver : Optional[resolver.Resolver] = None) -> resolver.Answer:
...

269
src/dns/edns.py Normal file
View File

@@ -0,0 +1,269 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2009-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""EDNS Options"""
from __future__ import absolute_import
import math
import struct
import dns.inet
#: NSID
NSID = 3
#: DAU
DAU = 5
#: DHU
DHU = 6
#: N3U
N3U = 7
#: ECS (client-subnet)
ECS = 8
#: EXPIRE
EXPIRE = 9
#: COOKIE
COOKIE = 10
#: KEEPALIVE
KEEPALIVE = 11
#: PADDING
PADDING = 12
#: CHAIN
CHAIN = 13
class Option(object):
"""Base class for all EDNS option types."""
def __init__(self, otype):
"""Initialize an option.
*otype*, an ``int``, is the option type.
"""
self.otype = otype
def to_wire(self, file):
"""Convert an option to wire format.
"""
raise NotImplementedError
@classmethod
def from_wire(cls, otype, wire, current, olen):
"""Build an EDNS option object from wire format.
*otype*, an ``int``, is the option type.
*wire*, a ``binary``, is the wire-format message.
*current*, an ``int``, is the offset in *wire* of the beginning
of the rdata.
*olen*, an ``int``, is the length of the wire-format option data
Returns a ``dns.edns.Option``.
"""
raise NotImplementedError
def _cmp(self, other):
"""Compare an EDNS option with another option of the same type.
Returns < 0 if < *other*, 0 if == *other*, and > 0 if > *other*.
"""
raise NotImplementedError
def __eq__(self, other):
if not isinstance(other, Option):
return False
if self.otype != other.otype:
return False
return self._cmp(other) == 0
def __ne__(self, other):
if not isinstance(other, Option):
return False
if self.otype != other.otype:
return False
return self._cmp(other) != 0
def __lt__(self, other):
if not isinstance(other, Option) or \
self.otype != other.otype:
return NotImplemented
return self._cmp(other) < 0
def __le__(self, other):
if not isinstance(other, Option) or \
self.otype != other.otype:
return NotImplemented
return self._cmp(other) <= 0
def __ge__(self, other):
if not isinstance(other, Option) or \
self.otype != other.otype:
return NotImplemented
return self._cmp(other) >= 0
def __gt__(self, other):
if not isinstance(other, Option) or \
self.otype != other.otype:
return NotImplemented
return self._cmp(other) > 0
class GenericOption(Option):
"""Generic Option Class
This class is used for EDNS option types for which we have no better
implementation.
"""
def __init__(self, otype, data):
super(GenericOption, self).__init__(otype)
self.data = data
def to_wire(self, file):
file.write(self.data)
def to_text(self):
return "Generic %d" % self.otype
@classmethod
def from_wire(cls, otype, wire, current, olen):
return cls(otype, wire[current: current + olen])
def _cmp(self, other):
if self.data == other.data:
return 0
if self.data > other.data:
return 1
return -1
class ECSOption(Option):
"""EDNS Client Subnet (ECS, RFC7871)"""
def __init__(self, address, srclen=None, scopelen=0):
"""*address*, a ``text``, is the client address information.
*srclen*, an ``int``, the source prefix length, which is the
leftmost number of bits of the address to be used for the
lookup. The default is 24 for IPv4 and 56 for IPv6.
*scopelen*, an ``int``, the scope prefix length. This value
must be 0 in queries, and should be set in responses.
"""
super(ECSOption, self).__init__(ECS)
af = dns.inet.af_for_address(address)
if af == dns.inet.AF_INET6:
self.family = 2
if srclen is None:
srclen = 56
elif af == dns.inet.AF_INET:
self.family = 1
if srclen is None:
srclen = 24
else:
raise ValueError('Bad ip family')
self.address = address
self.srclen = srclen
self.scopelen = scopelen
addrdata = dns.inet.inet_pton(af, address)
nbytes = int(math.ceil(srclen/8.0))
# Truncate to srclen and pad to the end of the last octet needed
# See RFC section 6
self.addrdata = addrdata[:nbytes]
nbits = srclen % 8
if nbits != 0:
last = struct.pack('B', ord(self.addrdata[-1:]) & (0xff << nbits))
self.addrdata = self.addrdata[:-1] + last
def to_text(self):
return "ECS {}/{} scope/{}".format(self.address, self.srclen,
self.scopelen)
def to_wire(self, file):
file.write(struct.pack('!H', self.family))
file.write(struct.pack('!BB', self.srclen, self.scopelen))
file.write(self.addrdata)
@classmethod
def from_wire(cls, otype, wire, cur, olen):
family, src, scope = struct.unpack('!HBB', wire[cur:cur+4])
cur += 4
addrlen = int(math.ceil(src/8.0))
if family == 1:
af = dns.inet.AF_INET
pad = 4 - addrlen
elif family == 2:
af = dns.inet.AF_INET6
pad = 16 - addrlen
else:
raise ValueError('unsupported family')
addr = dns.inet.inet_ntop(af, wire[cur:cur+addrlen] + b'\x00' * pad)
return cls(addr, src, scope)
def _cmp(self, other):
if self.addrdata == other.addrdata:
return 0
if self.addrdata > other.addrdata:
return 1
return -1
_type_to_class = {
ECS: ECSOption
}
def get_option_class(otype):
"""Return the class for the specified option type.
The GenericOption class is used if a more specific class is not
known.
"""
cls = _type_to_class.get(otype)
if cls is None:
cls = GenericOption
return cls
def option_from_wire(otype, wire, current, olen):
"""Build an EDNS option object from wire format.
*otype*, an ``int``, is the option type.
*wire*, a ``binary``, is the wire-format message.
*current*, an ``int``, is the offset in *wire* of the beginning
of the rdata.
*olen*, an ``int``, is the length of the wire-format option data
Returns an instance of a subclass of ``dns.edns.Option``.
"""
cls = get_option_class(otype)
return cls.from_wire(otype, wire, current, olen)

148
src/dns/entropy.py Normal file
View File

@@ -0,0 +1,148 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2009-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import os
import random
import time
from ._compat import long, binary_type
try:
import threading as _threading
except ImportError:
import dummy_threading as _threading
class EntropyPool(object):
# This is an entropy pool for Python implementations that do not
# have a working SystemRandom. I'm not sure there are any, but
# leaving this code doesn't hurt anything as the library code
# is used if present.
def __init__(self, seed=None):
self.pool_index = 0
self.digest = None
self.next_byte = 0
self.lock = _threading.Lock()
try:
import hashlib
self.hash = hashlib.sha1()
self.hash_len = 20
except ImportError:
try:
import sha
self.hash = sha.new()
self.hash_len = 20
except ImportError:
import md5 # pylint: disable=import-error
self.hash = md5.new()
self.hash_len = 16
self.pool = bytearray(b'\0' * self.hash_len)
if seed is not None:
self.stir(bytearray(seed))
self.seeded = True
self.seed_pid = os.getpid()
else:
self.seeded = False
self.seed_pid = 0
def stir(self, entropy, already_locked=False):
if not already_locked:
self.lock.acquire()
try:
for c in entropy:
if self.pool_index == self.hash_len:
self.pool_index = 0
b = c & 0xff
self.pool[self.pool_index] ^= b
self.pool_index += 1
finally:
if not already_locked:
self.lock.release()
def _maybe_seed(self):
if not self.seeded or self.seed_pid != os.getpid():
try:
seed = os.urandom(16)
except Exception:
try:
r = open('/dev/urandom', 'rb', 0)
try:
seed = r.read(16)
finally:
r.close()
except Exception:
seed = str(time.time())
self.seeded = True
self.seed_pid = os.getpid()
self.digest = None
seed = bytearray(seed)
self.stir(seed, True)
def random_8(self):
self.lock.acquire()
try:
self._maybe_seed()
if self.digest is None or self.next_byte == self.hash_len:
self.hash.update(binary_type(self.pool))
self.digest = bytearray(self.hash.digest())
self.stir(self.digest, True)
self.next_byte = 0
value = self.digest[self.next_byte]
self.next_byte += 1
finally:
self.lock.release()
return value
def random_16(self):
return self.random_8() * 256 + self.random_8()
def random_32(self):
return self.random_16() * 65536 + self.random_16()
def random_between(self, first, last):
size = last - first + 1
if size > long(4294967296):
raise ValueError('too big')
if size > 65536:
rand = self.random_32
max = long(4294967295)
elif size > 256:
rand = self.random_16
max = 65535
else:
rand = self.random_8
max = 255
return first + size * rand() // (max + 1)
pool = EntropyPool()
try:
system_random = random.SystemRandom()
except Exception:
system_random = None
def random_16():
if system_random is not None:
return system_random.randrange(0, 65536)
else:
return pool.random_16()
def between(first, last):
if system_random is not None:
return system_random.randrange(first, last + 1)
else:
return pool.random_between(first, last)

10
src/dns/entropy.pyi Normal file
View File

@@ -0,0 +1,10 @@
from typing import Optional
from random import SystemRandom
system_random : Optional[SystemRandom]
def random_16() -> int:
pass
def between(first: int, last: int) -> int:
pass

128
src/dns/exception.py Normal file
View File

@@ -0,0 +1,128 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""Common DNS Exceptions.
Dnspython modules may also define their own exceptions, which will
always be subclasses of ``DNSException``.
"""
class DNSException(Exception):
"""Abstract base class shared by all dnspython exceptions.
It supports two basic modes of operation:
a) Old/compatible mode is used if ``__init__`` was called with
empty *kwargs*. In compatible mode all *args* are passed
to the standard Python Exception class as before and all *args* are
printed by the standard ``__str__`` implementation. Class variable
``msg`` (or doc string if ``msg`` is ``None``) is returned from ``str()``
if *args* is empty.
b) New/parametrized mode is used if ``__init__`` was called with
non-empty *kwargs*.
In the new mode *args* must be empty and all kwargs must match
those set in class variable ``supp_kwargs``. All kwargs are stored inside
``self.kwargs`` and used in a new ``__str__`` implementation to construct
a formatted message based on the ``fmt`` class variable, a ``string``.
In the simplest case it is enough to override the ``supp_kwargs``
and ``fmt`` class variables to get nice parametrized messages.
"""
msg = None # non-parametrized message
supp_kwargs = set() # accepted parameters for _fmt_kwargs (sanity check)
fmt = None # message parametrized with results from _fmt_kwargs
def __init__(self, *args, **kwargs):
self._check_params(*args, **kwargs)
if kwargs:
self.kwargs = self._check_kwargs(**kwargs)
self.msg = str(self)
else:
self.kwargs = dict() # defined but empty for old mode exceptions
if self.msg is None:
# doc string is better implicit message than empty string
self.msg = self.__doc__
if args:
super(DNSException, self).__init__(*args)
else:
super(DNSException, self).__init__(self.msg)
def _check_params(self, *args, **kwargs):
"""Old exceptions supported only args and not kwargs.
For sanity we do not allow to mix old and new behavior."""
if args or kwargs:
assert bool(args) != bool(kwargs), \
'keyword arguments are mutually exclusive with positional args'
def _check_kwargs(self, **kwargs):
if kwargs:
assert set(kwargs.keys()) == self.supp_kwargs, \
'following set of keyword args is required: %s' % (
self.supp_kwargs)
return kwargs
def _fmt_kwargs(self, **kwargs):
"""Format kwargs before printing them.
Resulting dictionary has to have keys necessary for str.format call
on fmt class variable.
"""
fmtargs = {}
for kw, data in kwargs.items():
if isinstance(data, (list, set)):
# convert list of <someobj> to list of str(<someobj>)
fmtargs[kw] = list(map(str, data))
if len(fmtargs[kw]) == 1:
# remove list brackets [] from single-item lists
fmtargs[kw] = fmtargs[kw].pop()
else:
fmtargs[kw] = data
return fmtargs
def __str__(self):
if self.kwargs and self.fmt:
# provide custom message constructed from keyword arguments
fmtargs = self._fmt_kwargs(**self.kwargs)
return self.fmt.format(**fmtargs)
else:
# print *args directly in the same way as old DNSException
return super(DNSException, self).__str__()
class FormError(DNSException):
"""DNS message is malformed."""
class SyntaxError(DNSException):
"""Text input is malformed."""
class UnexpectedEnd(SyntaxError):
"""Text input ended unexpectedly."""
class TooBig(DNSException):
"""The DNS message is too big."""
class Timeout(DNSException):
"""The DNS operation timed out."""
supp_kwargs = {'timeout'}
fmt = "The DNS operation timed out after {timeout} seconds"

9
src/dns/exception.pyi Normal file
View File

@@ -0,0 +1,9 @@
from typing import Set, Optional, Dict
class DNSException(Exception):
supp_kwargs : Set[str]
kwargs : Optional[Dict]
class SyntaxError(DNSException): ...
class FormError(DNSException): ...
class Timeout(DNSException): ...

130
src/dns/flags.py Normal file
View File

@@ -0,0 +1,130 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2001-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""DNS Message Flags."""
# Standard DNS flags
#: Query Response
QR = 0x8000
#: Authoritative Answer
AA = 0x0400
#: Truncated Response
TC = 0x0200
#: Recursion Desired
RD = 0x0100
#: Recursion Available
RA = 0x0080
#: Authentic Data
AD = 0x0020
#: Checking Disabled
CD = 0x0010
# EDNS flags
#: DNSSEC answer OK
DO = 0x8000
_by_text = {
'QR': QR,
'AA': AA,
'TC': TC,
'RD': RD,
'RA': RA,
'AD': AD,
'CD': CD
}
_edns_by_text = {
'DO': DO
}
# We construct the inverse mappings programmatically to ensure that we
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
# would cause the mappings not to be true inverses.
_by_value = {y: x for x, y in _by_text.items()}
_edns_by_value = {y: x for x, y in _edns_by_text.items()}
def _order_flags(table):
order = list(table.items())
order.sort()
order.reverse()
return order
_flags_order = _order_flags(_by_value)
_edns_flags_order = _order_flags(_edns_by_value)
def _from_text(text, table):
flags = 0
tokens = text.split()
for t in tokens:
flags = flags | table[t.upper()]
return flags
def _to_text(flags, table, order):
text_flags = []
for k, v in order:
if flags & k != 0:
text_flags.append(v)
return ' '.join(text_flags)
def from_text(text):
"""Convert a space-separated list of flag text values into a flags
value.
Returns an ``int``
"""
return _from_text(text, _by_text)
def to_text(flags):
"""Convert a flags value into a space-separated list of flag text
values.
Returns a ``text``.
"""
return _to_text(flags, _by_value, _flags_order)
def edns_from_text(text):
"""Convert a space-separated list of EDNS flag text values into a EDNS
flags value.
Returns an ``int``
"""
return _from_text(text, _edns_by_text)
def edns_to_text(flags):
"""Convert an EDNS flags value into a space-separated list of EDNS flag
text values.
Returns a ``text``.
"""
return _to_text(flags, _edns_by_value, _edns_flags_order)

69
src/dns/grange.py Normal file
View File

@@ -0,0 +1,69 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2012-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""DNS GENERATE range conversion."""
import dns
def from_text(text):
"""Convert the text form of a range in a ``$GENERATE`` statement to an
integer.
*text*, a ``str``, the textual range in ``$GENERATE`` form.
Returns a tuple of three ``int`` values ``(start, stop, step)``.
"""
# TODO, figure out the bounds on start, stop and step.
step = 1
cur = ''
state = 0
# state 0 1 2 3 4
# x - y / z
if text and text[0] == '-':
raise dns.exception.SyntaxError("Start cannot be a negative number")
for c in text:
if c == '-' and state == 0:
start = int(cur)
cur = ''
state = 2
elif c == '/':
stop = int(cur)
cur = ''
state = 4
elif c.isdigit():
cur += c
else:
raise dns.exception.SyntaxError("Could not parse %s" % (c))
if state in (1, 3):
raise dns.exception.SyntaxError()
if state == 2:
stop = int(cur)
if state == 4:
step = int(cur)
assert step >= 1
assert start >= 0
assert start <= stop
# TODO, can start == stop?
return (start, stop, step)

37
src/dns/hash.py Normal file
View File

@@ -0,0 +1,37 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""Hashing backwards compatibility wrapper"""
import hashlib
import warnings
warnings.warn(
"dns.hash module will be removed in future versions. Please use hashlib instead.",
DeprecationWarning)
hashes = {}
hashes['MD5'] = hashlib.md5
hashes['SHA1'] = hashlib.sha1
hashes['SHA224'] = hashlib.sha224
hashes['SHA256'] = hashlib.sha256
hashes['SHA384'] = hashlib.sha384
hashes['SHA512'] = hashlib.sha512
def get(algorithm):
return hashes[algorithm.upper()]

124
src/dns/inet.py Normal file
View File

@@ -0,0 +1,124 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""Generic Internet address helper functions."""
import socket
import dns.ipv4
import dns.ipv6
from ._compat import maybe_ord
# We assume that AF_INET is always defined.
AF_INET = socket.AF_INET
# AF_INET6 might not be defined in the socket module, but we need it.
# We'll try to use the socket module's value, and if it doesn't work,
# we'll use our own value.
try:
AF_INET6 = socket.AF_INET6
except AttributeError:
AF_INET6 = 9999
def inet_pton(family, text):
"""Convert the textual form of a network address into its binary form.
*family* is an ``int``, the address family.
*text* is a ``text``, the textual address.
Raises ``NotImplementedError`` if the address family specified is not
implemented.
Returns a ``binary``.
"""
if family == AF_INET:
return dns.ipv4.inet_aton(text)
elif family == AF_INET6:
return dns.ipv6.inet_aton(text)
else:
raise NotImplementedError
def inet_ntop(family, address):
"""Convert the binary form of a network address into its textual form.
*family* is an ``int``, the address family.
*address* is a ``binary``, the network address in binary form.
Raises ``NotImplementedError`` if the address family specified is not
implemented.
Returns a ``text``.
"""
if family == AF_INET:
return dns.ipv4.inet_ntoa(address)
elif family == AF_INET6:
return dns.ipv6.inet_ntoa(address)
else:
raise NotImplementedError
def af_for_address(text):
"""Determine the address family of a textual-form network address.
*text*, a ``text``, the textual address.
Raises ``ValueError`` if the address family cannot be determined
from the input.
Returns an ``int``.
"""
try:
dns.ipv4.inet_aton(text)
return AF_INET
except Exception:
try:
dns.ipv6.inet_aton(text)
return AF_INET6
except:
raise ValueError
def is_multicast(text):
"""Is the textual-form network address a multicast address?
*text*, a ``text``, the textual address.
Raises ``ValueError`` if the address family cannot be determined
from the input.
Returns a ``bool``.
"""
try:
first = maybe_ord(dns.ipv4.inet_aton(text)[0])
return first >= 224 and first <= 239
except Exception:
try:
first = maybe_ord(dns.ipv6.inet_aton(text)[0])
return first == 255
except Exception:
raise ValueError

4
src/dns/inet.pyi Normal file
View File

@@ -0,0 +1,4 @@
from typing import Union
from socket import AddressFamily
AF_INET6 : Union[int, AddressFamily]

63
src/dns/ipv4.py Normal file
View File

@@ -0,0 +1,63 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""IPv4 helper functions."""
import struct
import dns.exception
from ._compat import binary_type
def inet_ntoa(address):
"""Convert an IPv4 address in binary form to text form.
*address*, a ``binary``, the IPv4 address in binary form.
Returns a ``text``.
"""
if len(address) != 4:
raise dns.exception.SyntaxError
if not isinstance(address, bytearray):
address = bytearray(address)
return ('%u.%u.%u.%u' % (address[0], address[1],
address[2], address[3]))
def inet_aton(text):
"""Convert an IPv4 address in text form to binary form.
*text*, a ``text``, the IPv4 address in textual form.
Returns a ``binary``.
"""
if not isinstance(text, binary_type):
text = text.encode()
parts = text.split(b'.')
if len(parts) != 4:
raise dns.exception.SyntaxError
for part in parts:
if not part.isdigit():
raise dns.exception.SyntaxError
if len(part) > 1 and part[0] == '0':
# No leading zeros
raise dns.exception.SyntaxError
try:
bytes = [int(part) for part in parts]
return struct.pack('BBBB', *bytes)
except:
raise dns.exception.SyntaxError

181
src/dns/ipv6.py Normal file
View File

@@ -0,0 +1,181 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""IPv6 helper functions."""
import re
import binascii
import dns.exception
import dns.ipv4
from ._compat import xrange, binary_type, maybe_decode
_leading_zero = re.compile(r'0+([0-9a-f]+)')
def inet_ntoa(address):
"""Convert an IPv6 address in binary form to text form.
*address*, a ``binary``, the IPv6 address in binary form.
Raises ``ValueError`` if the address isn't 16 bytes long.
Returns a ``text``.
"""
if len(address) != 16:
raise ValueError("IPv6 addresses are 16 bytes long")
hex = binascii.hexlify(address)
chunks = []
i = 0
l = len(hex)
while i < l:
chunk = maybe_decode(hex[i : i + 4])
# strip leading zeros. we do this with an re instead of
# with lstrip() because lstrip() didn't support chars until
# python 2.2.2
m = _leading_zero.match(chunk)
if not m is None:
chunk = m.group(1)
chunks.append(chunk)
i += 4
#
# Compress the longest subsequence of 0-value chunks to ::
#
best_start = 0
best_len = 0
start = -1
last_was_zero = False
for i in xrange(8):
if chunks[i] != '0':
if last_was_zero:
end = i
current_len = end - start
if current_len > best_len:
best_start = start
best_len = current_len
last_was_zero = False
elif not last_was_zero:
start = i
last_was_zero = True
if last_was_zero:
end = 8
current_len = end - start
if current_len > best_len:
best_start = start
best_len = current_len
if best_len > 1:
if best_start == 0 and \
(best_len == 6 or
best_len == 5 and chunks[5] == 'ffff'):
# We have an embedded IPv4 address
if best_len == 6:
prefix = '::'
else:
prefix = '::ffff:'
hex = prefix + dns.ipv4.inet_ntoa(address[12:])
else:
hex = ':'.join(chunks[:best_start]) + '::' + \
':'.join(chunks[best_start + best_len:])
else:
hex = ':'.join(chunks)
return hex
_v4_ending = re.compile(br'(.*):(\d+\.\d+\.\d+\.\d+)$')
_colon_colon_start = re.compile(br'::.*')
_colon_colon_end = re.compile(br'.*::$')
def inet_aton(text):
"""Convert an IPv6 address in text form to binary form.
*text*, a ``text``, the IPv6 address in textual form.
Returns a ``binary``.
"""
#
# Our aim here is not something fast; we just want something that works.
#
if not isinstance(text, binary_type):
text = text.encode()
if text == b'::':
text = b'0::'
#
# Get rid of the icky dot-quad syntax if we have it.
#
m = _v4_ending.match(text)
if not m is None:
b = bytearray(dns.ipv4.inet_aton(m.group(2)))
text = (u"{}:{:02x}{:02x}:{:02x}{:02x}".format(m.group(1).decode(),
b[0], b[1], b[2],
b[3])).encode()
#
# Try to turn '::<whatever>' into ':<whatever>'; if no match try to
# turn '<whatever>::' into '<whatever>:'
#
m = _colon_colon_start.match(text)
if not m is None:
text = text[1:]
else:
m = _colon_colon_end.match(text)
if not m is None:
text = text[:-1]
#
# Now canonicalize into 8 chunks of 4 hex digits each
#
chunks = text.split(b':')
l = len(chunks)
if l > 8:
raise dns.exception.SyntaxError
seen_empty = False
canonical = []
for c in chunks:
if c == b'':
if seen_empty:
raise dns.exception.SyntaxError
seen_empty = True
for i in xrange(0, 8 - l + 1):
canonical.append(b'0000')
else:
lc = len(c)
if lc > 4:
raise dns.exception.SyntaxError
if lc != 4:
c = (b'0' * (4 - lc)) + c
canonical.append(c)
if l < 8 and not seen_empty:
raise dns.exception.SyntaxError
text = b''.join(canonical)
#
# Finally we can go to binary.
#
try:
return binascii.unhexlify(text)
except (binascii.Error, TypeError):
raise dns.exception.SyntaxError
_mapped_prefix = b'\x00' * 10 + b'\xff\xff'
def is_mapped(address):
"""Is the specified address a mapped IPv4 address?
*address*, a ``binary`` is an IPv6 address in binary form.
Returns a ``bool``.
"""
return address.startswith(_mapped_prefix)

1175
src/dns/message.py Normal file

File diff suppressed because it is too large Load Diff

55
src/dns/message.pyi Normal file
View File

@@ -0,0 +1,55 @@
from typing import Optional, Dict, List, Tuple, Union
from . import name, rrset, tsig, rdatatype, entropy, edns, rdataclass
import hmac
class Message:
def to_wire(self, origin : Optional[name.Name]=None, max_size=0, **kw) -> bytes:
...
def find_rrset(self, section : List[rrset.RRset], name : name.Name, rdclass : int, rdtype : int,
covers=rdatatype.NONE, deleting : Optional[int]=None, create=False,
force_unique=False) -> rrset.RRset:
...
def __init__(self, id : Optional[int] =None) -> None:
self.id : int
self.flags = 0
self.question : List[rrset.RRset] = []
self.answer : List[rrset.RRset] = []
self.authority : List[rrset.RRset] = []
self.additional : List[rrset.RRset] = []
self.edns = -1
self.ednsflags = 0
self.payload = 0
self.options : List[edns.Option] = []
self.request_payload = 0
self.keyring = None
self.keyname = None
self.keyalgorithm = tsig.default_algorithm
self.request_mac = b''
self.other_data = b''
self.tsig_error = 0
self.fudge = 300
self.original_id = self.id
self.mac = b''
self.xfr = False
self.origin = None
self.tsig_ctx = None
self.had_tsig = False
self.multi = False
self.first = True
self.index : Dict[Tuple[rrset.RRset, name.Name, int, int, Union[int,str], int], rrset.RRset] = {}
def from_text(a : str) -> Message:
...
def from_wire(wire, keyring : Optional[Dict[name.Name,bytes]] = None, request_mac = b'', xfr=False, origin=None,
tsig_ctx : Optional[hmac.HMAC] = None, multi=False, first=True,
question_only=False, one_rr_per_rrset=False,
ignore_trailing=False) -> Message:
...
def make_response(query : Message, recursion_available=False, our_payload=8192,
fudge=300) -> Message:
...
def make_query(qname : Union[name.Name,str], rdtype : Union[str,int], rdclass : Union[int,str] =rdataclass.IN, use_edns : Optional[bool] = None,
want_dnssec=False, ednsflags : Optional[int] = None, payload : Optional[int] = None,
request_payload : Optional[int] = None, options : Optional[List[edns.Option]] = None) -> Message:
...

994
src/dns/name.py Normal file
View File

@@ -0,0 +1,994 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2001-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""DNS Names.
"""
from io import BytesIO
import struct
import sys
import copy
import encodings.idna
try:
import idna
have_idna_2008 = True
except ImportError:
have_idna_2008 = False
import dns.exception
import dns.wiredata
from ._compat import long, binary_type, text_type, unichr, maybe_decode
try:
maxint = sys.maxint # pylint: disable=sys-max-int
except AttributeError:
maxint = (1 << (8 * struct.calcsize("P"))) // 2 - 1
# fullcompare() result values
#: The compared names have no relationship to each other.
NAMERELN_NONE = 0
#: the first name is a superdomain of the second.
NAMERELN_SUPERDOMAIN = 1
#: The first name is a subdomain of the second.
NAMERELN_SUBDOMAIN = 2
#: The compared names are equal.
NAMERELN_EQUAL = 3
#: The compared names have a common ancestor.
NAMERELN_COMMONANCESTOR = 4
class EmptyLabel(dns.exception.SyntaxError):
"""A DNS label is empty."""
class BadEscape(dns.exception.SyntaxError):
"""An escaped code in a text format of DNS name is invalid."""
class BadPointer(dns.exception.FormError):
"""A DNS compression pointer points forward instead of backward."""
class BadLabelType(dns.exception.FormError):
"""The label type in DNS name wire format is unknown."""
class NeedAbsoluteNameOrOrigin(dns.exception.DNSException):
"""An attempt was made to convert a non-absolute name to
wire when there was also a non-absolute (or missing) origin."""
class NameTooLong(dns.exception.FormError):
"""A DNS name is > 255 octets long."""
class LabelTooLong(dns.exception.SyntaxError):
"""A DNS label is > 63 octets long."""
class AbsoluteConcatenation(dns.exception.DNSException):
"""An attempt was made to append anything other than the
empty name to an absolute DNS name."""
class NoParent(dns.exception.DNSException):
"""An attempt was made to get the parent of the root name
or the empty name."""
class NoIDNA2008(dns.exception.DNSException):
"""IDNA 2008 processing was requested but the idna module is not
available."""
class IDNAException(dns.exception.DNSException):
"""IDNA processing raised an exception."""
supp_kwargs = {'idna_exception'}
fmt = "IDNA processing exception: {idna_exception}"
class IDNACodec(object):
"""Abstract base class for IDNA encoder/decoders."""
def __init__(self):
pass
def encode(self, label):
raise NotImplementedError
def decode(self, label):
# We do not apply any IDNA policy on decode; we just
downcased = label.lower()
if downcased.startswith(b'xn--'):
try:
label = downcased[4:].decode('punycode')
except Exception as e:
raise IDNAException(idna_exception=e)
else:
label = maybe_decode(label)
return _escapify(label, True)
class IDNA2003Codec(IDNACodec):
"""IDNA 2003 encoder/decoder."""
def __init__(self, strict_decode=False):
"""Initialize the IDNA 2003 encoder/decoder.
*strict_decode* is a ``bool``. If `True`, then IDNA2003 checking
is done when decoding. This can cause failures if the name
was encoded with IDNA2008. The default is `False`.
"""
super(IDNA2003Codec, self).__init__()
self.strict_decode = strict_decode
def encode(self, label):
"""Encode *label*."""
if label == '':
return b''
try:
return encodings.idna.ToASCII(label)
except UnicodeError:
raise LabelTooLong
def decode(self, label):
"""Decode *label*."""
if not self.strict_decode:
return super(IDNA2003Codec, self).decode(label)
if label == b'':
return u''
try:
return _escapify(encodings.idna.ToUnicode(label), True)
except Exception as e:
raise IDNAException(idna_exception=e)
class IDNA2008Codec(IDNACodec):
"""IDNA 2008 encoder/decoder.
*uts_46* is a ``bool``. If True, apply Unicode IDNA
compatibility processing as described in Unicode Technical
Standard #46 (http://unicode.org/reports/tr46/).
If False, do not apply the mapping. The default is False.
*transitional* is a ``bool``: If True, use the
"transitional" mode described in Unicode Technical Standard
#46. The default is False.
*allow_pure_ascii* is a ``bool``. If True, then a label which
consists of only ASCII characters is allowed. This is less
strict than regular IDNA 2008, but is also necessary for mixed
names, e.g. a name with starting with "_sip._tcp." and ending
in an IDN suffix which would otherwise be disallowed. The
default is False.
*strict_decode* is a ``bool``: If True, then IDNA2008 checking
is done when decoding. This can cause failures if the name
was encoded with IDNA2003. The default is False.
"""
def __init__(self, uts_46=False, transitional=False,
allow_pure_ascii=False, strict_decode=False):
"""Initialize the IDNA 2008 encoder/decoder."""
super(IDNA2008Codec, self).__init__()
self.uts_46 = uts_46
self.transitional = transitional
self.allow_pure_ascii = allow_pure_ascii
self.strict_decode = strict_decode
def is_all_ascii(self, label):
for c in label:
if ord(c) > 0x7f:
return False
return True
def encode(self, label):
if label == '':
return b''
if self.allow_pure_ascii and self.is_all_ascii(label):
return label.encode('ascii')
if not have_idna_2008:
raise NoIDNA2008
try:
if self.uts_46:
label = idna.uts46_remap(label, False, self.transitional)
return idna.alabel(label)
except idna.IDNAError as e:
raise IDNAException(idna_exception=e)
def decode(self, label):
if not self.strict_decode:
return super(IDNA2008Codec, self).decode(label)
if label == b'':
return u''
if not have_idna_2008:
raise NoIDNA2008
try:
if self.uts_46:
label = idna.uts46_remap(label, False, False)
return _escapify(idna.ulabel(label), True)
except idna.IDNAError as e:
raise IDNAException(idna_exception=e)
_escaped = bytearray(b'"().;\\@$')
IDNA_2003_Practical = IDNA2003Codec(False)
IDNA_2003_Strict = IDNA2003Codec(True)
IDNA_2003 = IDNA_2003_Practical
IDNA_2008_Practical = IDNA2008Codec(True, False, True, False)
IDNA_2008_UTS_46 = IDNA2008Codec(True, False, False, False)
IDNA_2008_Strict = IDNA2008Codec(False, False, False, True)
IDNA_2008_Transitional = IDNA2008Codec(True, True, False, False)
IDNA_2008 = IDNA_2008_Practical
def _escapify(label, unicode_mode=False):
"""Escape the characters in label which need it.
@param unicode_mode: escapify only special and whitespace (<= 0x20)
characters
@returns: the escaped string
@rtype: string"""
if not unicode_mode:
text = ''
if isinstance(label, text_type):
label = label.encode()
for c in bytearray(label):
if c in _escaped:
text += '\\' + chr(c)
elif c > 0x20 and c < 0x7F:
text += chr(c)
else:
text += '\\%03d' % c
return text.encode()
text = u''
if isinstance(label, binary_type):
label = label.decode()
for c in label:
if c > u'\x20' and c < u'\x7f':
text += c
else:
if c >= u'\x7f':
text += c
else:
text += u'\\%03d' % ord(c)
return text
def _validate_labels(labels):
"""Check for empty labels in the middle of a label sequence,
labels that are too long, and for too many labels.
Raises ``dns.name.NameTooLong`` if the name as a whole is too long.
Raises ``dns.name.EmptyLabel`` if a label is empty (i.e. the root
label) and appears in a position other than the end of the label
sequence
"""
l = len(labels)
total = 0
i = -1
j = 0
for label in labels:
ll = len(label)
total += ll + 1
if ll > 63:
raise LabelTooLong
if i < 0 and label == b'':
i = j
j += 1
if total > 255:
raise NameTooLong
if i >= 0 and i != l - 1:
raise EmptyLabel
def _maybe_convert_to_binary(label):
"""If label is ``text``, convert it to ``binary``. If it is already
``binary`` just return it.
"""
if isinstance(label, binary_type):
return label
if isinstance(label, text_type):
return label.encode()
raise ValueError
class Name(object):
"""A DNS name.
The dns.name.Name class represents a DNS name as a tuple of
labels. Each label is a `binary` in DNS wire format. Instances
of the class are immutable.
"""
__slots__ = ['labels']
def __init__(self, labels):
"""*labels* is any iterable whose values are ``text`` or ``binary``.
"""
labels = [_maybe_convert_to_binary(x) for x in labels]
super(Name, self).__setattr__('labels', tuple(labels))
_validate_labels(self.labels)
def __setattr__(self, name, value):
# Names are immutable
raise TypeError("object doesn't support attribute assignment")
def __copy__(self):
return Name(self.labels)
def __deepcopy__(self, memo):
return Name(copy.deepcopy(self.labels, memo))
def __getstate__(self):
# Names can be pickled
return {'labels': self.labels}
def __setstate__(self, state):
super(Name, self).__setattr__('labels', state['labels'])
_validate_labels(self.labels)
def is_absolute(self):
"""Is the most significant label of this name the root label?
Returns a ``bool``.
"""
return len(self.labels) > 0 and self.labels[-1] == b''
def is_wild(self):
"""Is this name wild? (I.e. Is the least significant label '*'?)
Returns a ``bool``.
"""
return len(self.labels) > 0 and self.labels[0] == b'*'
def __hash__(self):
"""Return a case-insensitive hash of the name.
Returns an ``int``.
"""
h = long(0)
for label in self.labels:
for c in bytearray(label.lower()):
h += (h << 3) + c
return int(h % maxint)
def fullcompare(self, other):
"""Compare two names, returning a 3-tuple
``(relation, order, nlabels)``.
*relation* describes the relation ship between the names,
and is one of: ``dns.name.NAMERELN_NONE``,
``dns.name.NAMERELN_SUPERDOMAIN``, ``dns.name.NAMERELN_SUBDOMAIN``,
``dns.name.NAMERELN_EQUAL``, or ``dns.name.NAMERELN_COMMONANCESTOR``.
*order* is < 0 if *self* < *other*, > 0 if *self* > *other*, and ==
0 if *self* == *other*. A relative name is always less than an
absolute name. If both names have the same relativity, then
the DNSSEC order relation is used to order them.
*nlabels* is the number of significant labels that the two names
have in common.
Here are some examples. Names ending in "." are absolute names,
those not ending in "." are relative names.
============= ============= =========== ===== =======
self other relation order nlabels
============= ============= =========== ===== =======
www.example. www.example. equal 0 3
www.example. example. subdomain > 0 2
example. www.example. superdomain < 0 2
example1.com. example2.com. common anc. < 0 2
example1 example2. none < 0 0
example1. example2 none > 0 0
============= ============= =========== ===== =======
"""
sabs = self.is_absolute()
oabs = other.is_absolute()
if sabs != oabs:
if sabs:
return (NAMERELN_NONE, 1, 0)
else:
return (NAMERELN_NONE, -1, 0)
l1 = len(self.labels)
l2 = len(other.labels)
ldiff = l1 - l2
if ldiff < 0:
l = l1
else:
l = l2
order = 0
nlabels = 0
namereln = NAMERELN_NONE
while l > 0:
l -= 1
l1 -= 1
l2 -= 1
label1 = self.labels[l1].lower()
label2 = other.labels[l2].lower()
if label1 < label2:
order = -1
if nlabels > 0:
namereln = NAMERELN_COMMONANCESTOR
return (namereln, order, nlabels)
elif label1 > label2:
order = 1
if nlabels > 0:
namereln = NAMERELN_COMMONANCESTOR
return (namereln, order, nlabels)
nlabels += 1
order = ldiff
if ldiff < 0:
namereln = NAMERELN_SUPERDOMAIN
elif ldiff > 0:
namereln = NAMERELN_SUBDOMAIN
else:
namereln = NAMERELN_EQUAL
return (namereln, order, nlabels)
def is_subdomain(self, other):
"""Is self a subdomain of other?
Note that the notion of subdomain includes equality, e.g.
"dnpython.org" is a subdomain of itself.
Returns a ``bool``.
"""
(nr, o, nl) = self.fullcompare(other)
if nr == NAMERELN_SUBDOMAIN or nr == NAMERELN_EQUAL:
return True
return False
def is_superdomain(self, other):
"""Is self a superdomain of other?
Note that the notion of superdomain includes equality, e.g.
"dnpython.org" is a superdomain of itself.
Returns a ``bool``.
"""
(nr, o, nl) = self.fullcompare(other)
if nr == NAMERELN_SUPERDOMAIN or nr == NAMERELN_EQUAL:
return True
return False
def canonicalize(self):
"""Return a name which is equal to the current name, but is in
DNSSEC canonical form.
"""
return Name([x.lower() for x in self.labels])
def __eq__(self, other):
if isinstance(other, Name):
return self.fullcompare(other)[1] == 0
else:
return False
def __ne__(self, other):
if isinstance(other, Name):
return self.fullcompare(other)[1] != 0
else:
return True
def __lt__(self, other):
if isinstance(other, Name):
return self.fullcompare(other)[1] < 0
else:
return NotImplemented
def __le__(self, other):
if isinstance(other, Name):
return self.fullcompare(other)[1] <= 0
else:
return NotImplemented
def __ge__(self, other):
if isinstance(other, Name):
return self.fullcompare(other)[1] >= 0
else:
return NotImplemented
def __gt__(self, other):
if isinstance(other, Name):
return self.fullcompare(other)[1] > 0
else:
return NotImplemented
def __repr__(self):
return '<DNS name ' + self.__str__() + '>'
def __str__(self):
return self.to_text(False)
def to_text(self, omit_final_dot=False):
"""Convert name to DNS text format.
*omit_final_dot* is a ``bool``. If True, don't emit the final
dot (denoting the root label) for absolute names. The default
is False.
Returns a ``text``.
"""
if len(self.labels) == 0:
return maybe_decode(b'@')
if len(self.labels) == 1 and self.labels[0] == b'':
return maybe_decode(b'.')
if omit_final_dot and self.is_absolute():
l = self.labels[:-1]
else:
l = self.labels
s = b'.'.join(map(_escapify, l))
return maybe_decode(s)
def to_unicode(self, omit_final_dot=False, idna_codec=None):
"""Convert name to Unicode text format.
IDN ACE labels are converted to Unicode.
*omit_final_dot* is a ``bool``. If True, don't emit the final
dot (denoting the root label) for absolute names. The default
is False.
*idna_codec* specifies the IDNA encoder/decoder. If None, the
dns.name.IDNA_2003_Practical encoder/decoder is used.
The IDNA_2003_Practical decoder does
not impose any policy, it just decodes punycode, so if you
don't want checking for compliance, you can use this decoder
for IDNA2008 as well.
Returns a ``text``.
"""
if len(self.labels) == 0:
return u'@'
if len(self.labels) == 1 and self.labels[0] == b'':
return u'.'
if omit_final_dot and self.is_absolute():
l = self.labels[:-1]
else:
l = self.labels
if idna_codec is None:
idna_codec = IDNA_2003_Practical
return u'.'.join([idna_codec.decode(x) for x in l])
def to_digestable(self, origin=None):
"""Convert name to a format suitable for digesting in hashes.
The name is canonicalized and converted to uncompressed wire
format. All names in wire format are absolute. If the name
is a relative name, then an origin must be supplied.
*origin* is a ``dns.name.Name`` or ``None``. If the name is
relative and origin is not ``None``, then origin will be appended
to the name.
Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is
relative and no origin was provided.
Returns a ``binary``.
"""
if not self.is_absolute():
if origin is None or not origin.is_absolute():
raise NeedAbsoluteNameOrOrigin
labels = list(self.labels)
labels.extend(list(origin.labels))
else:
labels = self.labels
dlabels = [struct.pack('!B%ds' % len(x), len(x), x.lower())
for x in labels]
return b''.join(dlabels)
def to_wire(self, file=None, compress=None, origin=None):
"""Convert name to wire format, possibly compressing it.
*file* is the file where the name is emitted (typically a
BytesIO file). If ``None`` (the default), a ``binary``
containing the wire name will be returned.
*compress*, a ``dict``, is the compression table to use. If
``None`` (the default), names will not be compressed.
*origin* is a ``dns.name.Name`` or ``None``. If the name is
relative and origin is not ``None``, then *origin* will be appended
to it.
Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is
relative and no origin was provided.
Returns a ``binary`` or ``None``.
"""
if file is None:
file = BytesIO()
want_return = True
else:
want_return = False
if not self.is_absolute():
if origin is None or not origin.is_absolute():
raise NeedAbsoluteNameOrOrigin
labels = list(self.labels)
labels.extend(list(origin.labels))
else:
labels = self.labels
i = 0
for label in labels:
n = Name(labels[i:])
i += 1
if compress is not None:
pos = compress.get(n)
else:
pos = None
if pos is not None:
value = 0xc000 + pos
s = struct.pack('!H', value)
file.write(s)
break
else:
if compress is not None and len(n) > 1:
pos = file.tell()
if pos <= 0x3fff:
compress[n] = pos
l = len(label)
file.write(struct.pack('!B', l))
if l > 0:
file.write(label)
if want_return:
return file.getvalue()
def __len__(self):
"""The length of the name (in labels).
Returns an ``int``.
"""
return len(self.labels)
def __getitem__(self, index):
return self.labels[index]
def __add__(self, other):
return self.concatenate(other)
def __sub__(self, other):
return self.relativize(other)
def split(self, depth):
"""Split a name into a prefix and suffix names at the specified depth.
*depth* is an ``int`` specifying the number of labels in the suffix
Raises ``ValueError`` if *depth* was not >= 0 and <= the length of the
name.
Returns the tuple ``(prefix, suffix)``.
"""
l = len(self.labels)
if depth == 0:
return (self, dns.name.empty)
elif depth == l:
return (dns.name.empty, self)
elif depth < 0 or depth > l:
raise ValueError(
'depth must be >= 0 and <= the length of the name')
return (Name(self[: -depth]), Name(self[-depth:]))
def concatenate(self, other):
"""Return a new name which is the concatenation of self and other.
Raises ``dns.name.AbsoluteConcatenation`` if the name is
absolute and *other* is not the empty name.
Returns a ``dns.name.Name``.
"""
if self.is_absolute() and len(other) > 0:
raise AbsoluteConcatenation
labels = list(self.labels)
labels.extend(list(other.labels))
return Name(labels)
def relativize(self, origin):
"""If the name is a subdomain of *origin*, return a new name which is
the name relative to origin. Otherwise return the name.
For example, relativizing ``www.dnspython.org.`` to origin
``dnspython.org.`` returns the name ``www``. Relativizing ``example.``
to origin ``dnspython.org.`` returns ``example.``.
Returns a ``dns.name.Name``.
"""
if origin is not None and self.is_subdomain(origin):
return Name(self[: -len(origin)])
else:
return self
def derelativize(self, origin):
"""If the name is a relative name, return a new name which is the
concatenation of the name and origin. Otherwise return the name.
For example, derelativizing ``www`` to origin ``dnspython.org.``
returns the name ``www.dnspython.org.``. Derelativizing ``example.``
to origin ``dnspython.org.`` returns ``example.``.
Returns a ``dns.name.Name``.
"""
if not self.is_absolute():
return self.concatenate(origin)
else:
return self
def choose_relativity(self, origin=None, relativize=True):
"""Return a name with the relativity desired by the caller.
If *origin* is ``None``, then the name is returned.
Otherwise, if *relativize* is ``True`` the name is
relativized, and if *relativize* is ``False`` the name is
derelativized.
Returns a ``dns.name.Name``.
"""
if origin:
if relativize:
return self.relativize(origin)
else:
return self.derelativize(origin)
else:
return self
def parent(self):
"""Return the parent of the name.
For example, the parent of ``www.dnspython.org.`` is ``dnspython.org``.
Raises ``dns.name.NoParent`` if the name is either the root name or the
empty name, and thus has no parent.
Returns a ``dns.name.Name``.
"""
if self == root or self == empty:
raise NoParent
return Name(self.labels[1:])
#: The root name, '.'
root = Name([b''])
#: The empty name.
empty = Name([])
def from_unicode(text, origin=root, idna_codec=None):
"""Convert unicode text into a Name object.
Labels are encoded in IDN ACE form according to rules specified by
the IDNA codec.
*text*, a ``text``, is the text to convert into a name.
*origin*, a ``dns.name.Name``, specifies the origin to
append to non-absolute names. The default is the root name.
*idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA
encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder
is used.
Returns a ``dns.name.Name``.
"""
if not isinstance(text, text_type):
raise ValueError("input to from_unicode() must be a unicode string")
if not (origin is None or isinstance(origin, Name)):
raise ValueError("origin must be a Name or None")
labels = []
label = u''
escaping = False
edigits = 0
total = 0
if idna_codec is None:
idna_codec = IDNA_2003
if text == u'@':
text = u''
if text:
if text == u'.':
return Name([b'']) # no Unicode "u" on this constant!
for c in text:
if escaping:
if edigits == 0:
if c.isdigit():
total = int(c)
edigits += 1
else:
label += c
escaping = False
else:
if not c.isdigit():
raise BadEscape
total *= 10
total += int(c)
edigits += 1
if edigits == 3:
escaping = False
label += unichr(total)
elif c in [u'.', u'\u3002', u'\uff0e', u'\uff61']:
if len(label) == 0:
raise EmptyLabel
labels.append(idna_codec.encode(label))
label = u''
elif c == u'\\':
escaping = True
edigits = 0
total = 0
else:
label += c
if escaping:
raise BadEscape
if len(label) > 0:
labels.append(idna_codec.encode(label))
else:
labels.append(b'')
if (len(labels) == 0 or labels[-1] != b'') and origin is not None:
labels.extend(list(origin.labels))
return Name(labels)
def from_text(text, origin=root, idna_codec=None):
"""Convert text into a Name object.
*text*, a ``text``, is the text to convert into a name.
*origin*, a ``dns.name.Name``, specifies the origin to
append to non-absolute names. The default is the root name.
*idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA
encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder
is used.
Returns a ``dns.name.Name``.
"""
if isinstance(text, text_type):
return from_unicode(text, origin, idna_codec)
if not isinstance(text, binary_type):
raise ValueError("input to from_text() must be a string")
if not (origin is None or isinstance(origin, Name)):
raise ValueError("origin must be a Name or None")
labels = []
label = b''
escaping = False
edigits = 0
total = 0
if text == b'@':
text = b''
if text:
if text == b'.':
return Name([b''])
for c in bytearray(text):
byte_ = struct.pack('!B', c)
if escaping:
if edigits == 0:
if byte_.isdigit():
total = int(byte_)
edigits += 1
else:
label += byte_
escaping = False
else:
if not byte_.isdigit():
raise BadEscape
total *= 10
total += int(byte_)
edigits += 1
if edigits == 3:
escaping = False
label += struct.pack('!B', total)
elif byte_ == b'.':
if len(label) == 0:
raise EmptyLabel
labels.append(label)
label = b''
elif byte_ == b'\\':
escaping = True
edigits = 0
total = 0
else:
label += byte_
if escaping:
raise BadEscape
if len(label) > 0:
labels.append(label)
else:
labels.append(b'')
if (len(labels) == 0 or labels[-1] != b'') and origin is not None:
labels.extend(list(origin.labels))
return Name(labels)
def from_wire(message, current):
"""Convert possibly compressed wire format into a Name.
*message* is a ``binary`` containing an entire DNS message in DNS
wire form.
*current*, an ``int``, is the offset of the beginning of the name
from the start of the message
Raises ``dns.name.BadPointer`` if a compression pointer did not
point backwards in the message.
Raises ``dns.name.BadLabelType`` if an invalid label type was encountered.
Returns a ``(dns.name.Name, int)`` tuple consisting of the name
that was read and the number of bytes of the wire format message
which were consumed reading it.
"""
if not isinstance(message, binary_type):
raise ValueError("input to from_wire() must be a byte string")
message = dns.wiredata.maybe_wrap(message)
labels = []
biggest_pointer = current
hops = 0
count = message[current]
current += 1
cused = 1
while count != 0:
if count < 64:
labels.append(message[current: current + count].unwrap())
current += count
if hops == 0:
cused += count
elif count >= 192:
current = (count & 0x3f) * 256 + message[current]
if hops == 0:
cused += 1
if current >= biggest_pointer:
raise BadPointer
biggest_pointer = current
hops += 1
else:
raise BadLabelType
count = message[current]
current += 1
if hops == 0:
cused += 1
labels.append('')
return (Name(labels), cused)

35
src/dns/name.pyi Normal file
View File

@@ -0,0 +1,35 @@
from typing import Optional, Union, Tuple, Iterable, List
class Name:
def is_subdomain(self, o : Name) -> bool: ...
def is_superdomain(self, o : Name) -> bool: ...
def __init__(self, labels : Iterable[Union[bytes,str]]) -> None:
self.labels : List[bytes]
def is_absolute(self) -> bool: ...
def is_wild(self) -> bool: ...
def fullcompare(self, other) -> Tuple[int,int,int]: ...
def canonicalize(self) -> Name: ...
def __lt__(self, other : Name): ...
def __le__(self, other : Name): ...
def __ge__(self, other : Name): ...
def __gt__(self, other : Name): ...
def to_text(self, omit_final_dot=False) -> str: ...
def to_unicode(self, omit_final_dot=False, idna_codec=None) -> str: ...
def to_digestable(self, origin=None) -> bytes: ...
def to_wire(self, file=None, compress=None, origin=None) -> Optional[bytes]: ...
def __add__(self, other : Name): ...
def __sub__(self, other : Name): ...
def split(self, depth) -> List[Tuple[str,str]]: ...
def concatenate(self, other : Name) -> Name: ...
def relativize(self, origin): ...
def derelativize(self, origin): ...
def choose_relativity(self, origin : Optional[Name] = None, relativize=True): ...
def parent(self) -> Name: ...
class IDNACodec:
pass
def from_text(text, origin : Optional[Name] = Name('.'), idna_codec : Optional[IDNACodec] = None) -> Name:
...
empty : Name

108
src/dns/namedict.py Normal file
View File

@@ -0,0 +1,108 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2017 Nominum, Inc.
# Copyright (C) 2016 Coresec Systems AB
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND CORESEC SYSTEMS AB DISCLAIMS ALL
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CORESEC
# SYSTEMS AB BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""DNS name dictionary"""
import collections
import dns.name
from ._compat import xrange
class NameDict(collections.MutableMapping):
"""A dictionary whose keys are dns.name.Name objects.
In addition to being like a regular Python dictionary, this
dictionary can also get the deepest match for a given key.
"""
__slots__ = ["max_depth", "max_depth_items", "__store"]
def __init__(self, *args, **kwargs):
super(NameDict, self).__init__()
self.__store = dict()
#: the maximum depth of the keys that have ever been added
self.max_depth = 0
#: the number of items of maximum depth
self.max_depth_items = 0
self.update(dict(*args, **kwargs))
def __update_max_depth(self, key):
if len(key) == self.max_depth:
self.max_depth_items = self.max_depth_items + 1
elif len(key) > self.max_depth:
self.max_depth = len(key)
self.max_depth_items = 1
def __getitem__(self, key):
return self.__store[key]
def __setitem__(self, key, value):
if not isinstance(key, dns.name.Name):
raise ValueError('NameDict key must be a name')
self.__store[key] = value
self.__update_max_depth(key)
def __delitem__(self, key):
value = self.__store.pop(key)
if len(value) == self.max_depth:
self.max_depth_items = self.max_depth_items - 1
if self.max_depth_items == 0:
self.max_depth = 0
for k in self.__store:
self.__update_max_depth(k)
def __iter__(self):
return iter(self.__store)
def __len__(self):
return len(self.__store)
def has_key(self, key):
return key in self.__store
def get_deepest_match(self, name):
"""Find the deepest match to *fname* in the dictionary.
The deepest match is the longest name in the dictionary which is
a superdomain of *name*. Note that *superdomain* includes matching
*name* itself.
*name*, a ``dns.name.Name``, the name to find.
Returns a ``(key, value)`` where *key* is the deepest
``dns.name.Name``, and *value* is the value associated with *key*.
"""
depth = len(name)
if depth > self.max_depth:
depth = self.max_depth
for i in xrange(-depth, 0):
n = dns.name.Name(name[i:])
if n in self:
return (n, self[n])
v = self[dns.name.empty]
return (dns.name.empty, v)

182
src/dns/node.py Normal file
View File

@@ -0,0 +1,182 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2001-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""DNS nodes. A node is a set of rdatasets."""
from io import StringIO
import dns.rdataset
import dns.rdatatype
import dns.renderer
class Node(object):
"""A Node is a set of rdatasets."""
__slots__ = ['rdatasets']
def __init__(self):
#: the set of rdatsets, represented as a list.
self.rdatasets = []
def to_text(self, name, **kw):
"""Convert a node to text format.
Each rdataset at the node is printed. Any keyword arguments
to this method are passed on to the rdataset's to_text() method.
*name*, a ``dns.name.Name`` or ``text``, the owner name of the rdatasets.
Returns a ``text``.
"""
s = StringIO()
for rds in self.rdatasets:
if len(rds) > 0:
s.write(rds.to_text(name, **kw))
s.write(u'\n')
return s.getvalue()[:-1]
def __repr__(self):
return '<DNS node ' + str(id(self)) + '>'
def __eq__(self, other):
#
# This is inefficient. Good thing we don't need to do it much.
#
for rd in self.rdatasets:
if rd not in other.rdatasets:
return False
for rd in other.rdatasets:
if rd not in self.rdatasets:
return False
return True
def __ne__(self, other):
return not self.__eq__(other)
def __len__(self):
return len(self.rdatasets)
def __iter__(self):
return iter(self.rdatasets)
def find_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE,
create=False):
"""Find an rdataset matching the specified properties in the
current node.
*rdclass*, an ``int``, the class of the rdataset.
*rdtype*, an ``int``, the type of the rdataset.
*covers*, an ``int``, the covered type. Usually this value is
dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or
dns.rdatatype.RRSIG, then the covers value will be the rdata
type the SIG/RRSIG covers. The library treats the SIG and RRSIG
types as if they were a family of
types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much
easier to work with than if RRSIGs covering different rdata
types were aggregated into a single RRSIG rdataset.
*create*, a ``bool``. If True, create the rdataset if it is not found.
Raises ``KeyError`` if an rdataset of the desired type and class does
not exist and *create* is not ``True``.
Returns a ``dns.rdataset.Rdataset``.
"""
for rds in self.rdatasets:
if rds.match(rdclass, rdtype, covers):
return rds
if not create:
raise KeyError
rds = dns.rdataset.Rdataset(rdclass, rdtype)
self.rdatasets.append(rds)
return rds
def get_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE,
create=False):
"""Get an rdataset matching the specified properties in the
current node.
None is returned if an rdataset of the specified type and
class does not exist and *create* is not ``True``.
*rdclass*, an ``int``, the class of the rdataset.
*rdtype*, an ``int``, the type of the rdataset.
*covers*, an ``int``, the covered type. Usually this value is
dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or
dns.rdatatype.RRSIG, then the covers value will be the rdata
type the SIG/RRSIG covers. The library treats the SIG and RRSIG
types as if they were a family of
types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much
easier to work with than if RRSIGs covering different rdata
types were aggregated into a single RRSIG rdataset.
*create*, a ``bool``. If True, create the rdataset if it is not found.
Returns a ``dns.rdataset.Rdataset`` or ``None``.
"""
try:
rds = self.find_rdataset(rdclass, rdtype, covers, create)
except KeyError:
rds = None
return rds
def delete_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE):
"""Delete the rdataset matching the specified properties in the
current node.
If a matching rdataset does not exist, it is not an error.
*rdclass*, an ``int``, the class of the rdataset.
*rdtype*, an ``int``, the type of the rdataset.
*covers*, an ``int``, the covered type.
"""
rds = self.get_rdataset(rdclass, rdtype, covers)
if rds is not None:
self.rdatasets.remove(rds)
def replace_rdataset(self, replacement):
"""Replace an rdataset.
It is not an error if there is no rdataset matching *replacement*.
Ownership of the *replacement* object is transferred to the node;
in other words, this method does not store a copy of *replacement*
at the node, it stores *replacement* itself.
*replacement*, a ``dns.rdataset.Rdataset``.
Raises ``ValueError`` if *replacement* is not a
``dns.rdataset.Rdataset``.
"""
if not isinstance(replacement, dns.rdataset.Rdataset):
raise ValueError('replacement is not an rdataset')
self.delete_rdataset(replacement.rdclass, replacement.rdtype,
replacement.covers)
self.rdatasets.append(replacement)

17
src/dns/node.pyi Normal file
View File

@@ -0,0 +1,17 @@
from typing import List, Optional, Union
from . import rdataset, rdatatype, name
class Node:
def __init__(self):
self.rdatasets : List[rdataset.Rdataset]
def to_text(self, name : Union[str,name.Name], **kw) -> str:
...
def find_rdataset(self, rdclass : int, rdtype : int, covers=rdatatype.NONE,
create=False) -> rdataset.Rdataset:
...
def get_rdataset(self, rdclass : int, rdtype : int, covers=rdatatype.NONE,
create=False) -> Optional[rdataset.Rdataset]:
...
def delete_rdataset(self, rdclass : int, rdtype : int, covers=rdatatype.NONE):
...
def replace_rdataset(self, replacement : rdataset.Rdataset) -> None:
...

119
src/dns/opcode.py Normal file
View File

@@ -0,0 +1,119 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2001-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""DNS Opcodes."""
import dns.exception
#: Query
QUERY = 0
#: Inverse Query (historical)
IQUERY = 1
#: Server Status (unspecified and unimplemented anywhere)
STATUS = 2
#: Notify
NOTIFY = 4
#: Dynamic Update
UPDATE = 5
_by_text = {
'QUERY': QUERY,
'IQUERY': IQUERY,
'STATUS': STATUS,
'NOTIFY': NOTIFY,
'UPDATE': UPDATE
}
# We construct the inverse mapping programmatically to ensure that we
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
# would cause the mapping not to be true inverse.
_by_value = {y: x for x, y in _by_text.items()}
class UnknownOpcode(dns.exception.DNSException):
"""An DNS opcode is unknown."""
def from_text(text):
"""Convert text into an opcode.
*text*, a ``text``, the textual opcode
Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown.
Returns an ``int``.
"""
if text.isdigit():
value = int(text)
if value >= 0 and value <= 15:
return value
value = _by_text.get(text.upper())
if value is None:
raise UnknownOpcode
return value
def from_flags(flags):
"""Extract an opcode from DNS message flags.
*flags*, an ``int``, the DNS flags.
Returns an ``int``.
"""
return (flags & 0x7800) >> 11
def to_flags(value):
"""Convert an opcode to a value suitable for ORing into DNS message
flags.
*value*, an ``int``, the DNS opcode value.
Returns an ``int``.
"""
return (value << 11) & 0x7800
def to_text(value):
"""Convert an opcode to text.
*value*, an ``int`` the opcode value,
Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown.
Returns a ``text``.
"""
text = _by_value.get(value)
if text is None:
text = str(value)
return text
def is_update(flags):
"""Is the opcode in flags UPDATE?
*flags*, an ``int``, the DNS message flags.
Returns a ``bool``.
"""
return from_flags(flags) == UPDATE

0
src/dns/py.typed Normal file
View File

683
src/dns/query.py Normal file
View File

@@ -0,0 +1,683 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""Talk to a DNS server."""
from __future__ import generators
import errno
import select
import socket
import struct
import sys
import time
import dns.exception
import dns.inet
import dns.name
import dns.message
import dns.rcode
import dns.rdataclass
import dns.rdatatype
from ._compat import long, string_types, PY3
if PY3:
select_error = OSError
else:
select_error = select.error
# Function used to create a socket. Can be overridden if needed in special
# situations.
socket_factory = socket.socket
class UnexpectedSource(dns.exception.DNSException):
"""A DNS query response came from an unexpected address or port."""
class BadResponse(dns.exception.FormError):
"""A DNS query response does not respond to the question asked."""
class TransferError(dns.exception.DNSException):
"""A zone transfer response got a non-zero rcode."""
def __init__(self, rcode):
message = 'Zone transfer error: %s' % dns.rcode.to_text(rcode)
super(TransferError, self).__init__(message)
self.rcode = rcode
def _compute_expiration(timeout):
if timeout is None:
return None
else:
return time.time() + timeout
# This module can use either poll() or select() as the "polling backend".
#
# A backend function takes an fd, bools for readability, writablity, and
# error detection, and a timeout.
def _poll_for(fd, readable, writable, error, timeout):
"""Poll polling backend."""
event_mask = 0
if readable:
event_mask |= select.POLLIN
if writable:
event_mask |= select.POLLOUT
if error:
event_mask |= select.POLLERR
pollable = select.poll()
pollable.register(fd, event_mask)
if timeout:
event_list = pollable.poll(long(timeout * 1000))
else:
event_list = pollable.poll()
return bool(event_list)
def _select_for(fd, readable, writable, error, timeout):
"""Select polling backend."""
rset, wset, xset = [], [], []
if readable:
rset = [fd]
if writable:
wset = [fd]
if error:
xset = [fd]
if timeout is None:
(rcount, wcount, xcount) = select.select(rset, wset, xset)
else:
(rcount, wcount, xcount) = select.select(rset, wset, xset, timeout)
return bool((rcount or wcount or xcount))
def _wait_for(fd, readable, writable, error, expiration):
# Use the selected polling backend to wait for any of the specified
# events. An "expiration" absolute time is converted into a relative
# timeout.
done = False
while not done:
if expiration is None:
timeout = None
else:
timeout = expiration - time.time()
if timeout <= 0.0:
raise dns.exception.Timeout
try:
if not _polling_backend(fd, readable, writable, error, timeout):
raise dns.exception.Timeout
except select_error as e:
if e.args[0] != errno.EINTR:
raise e
done = True
def _set_polling_backend(fn):
# Internal API. Do not use.
global _polling_backend
_polling_backend = fn
if hasattr(select, 'poll'):
# Prefer poll() on platforms that support it because it has no
# limits on the maximum value of a file descriptor (plus it will
# be more efficient for high values).
_polling_backend = _poll_for
else:
_polling_backend = _select_for
def _wait_for_readable(s, expiration):
_wait_for(s, True, False, True, expiration)
def _wait_for_writable(s, expiration):
_wait_for(s, False, True, True, expiration)
def _addresses_equal(af, a1, a2):
# Convert the first value of the tuple, which is a textual format
# address into binary form, so that we are not confused by different
# textual representations of the same address
try:
n1 = dns.inet.inet_pton(af, a1[0])
n2 = dns.inet.inet_pton(af, a2[0])
except dns.exception.SyntaxError:
return False
return n1 == n2 and a1[1:] == a2[1:]
def _destination_and_source(af, where, port, source, source_port):
# Apply defaults and compute destination and source tuples
# suitable for use in connect(), sendto(), or bind().
if af is None:
try:
af = dns.inet.af_for_address(where)
except Exception:
af = dns.inet.AF_INET
if af == dns.inet.AF_INET:
destination = (where, port)
if source is not None or source_port != 0:
if source is None:
source = '0.0.0.0'
source = (source, source_port)
elif af == dns.inet.AF_INET6:
destination = (where, port, 0, 0)
if source is not None or source_port != 0:
if source is None:
source = '::'
source = (source, source_port, 0, 0)
return (af, destination, source)
def send_udp(sock, what, destination, expiration=None):
"""Send a DNS message to the specified UDP socket.
*sock*, a ``socket``.
*what*, a ``binary`` or ``dns.message.Message``, the message to send.
*destination*, a destination tuple appropriate for the address family
of the socket, specifying where to send the query.
*expiration*, a ``float`` or ``None``, the absolute time at which
a timeout exception should be raised. If ``None``, no timeout will
occur.
Returns an ``(int, float)`` tuple of bytes sent and the sent time.
"""
if isinstance(what, dns.message.Message):
what = what.to_wire()
_wait_for_writable(sock, expiration)
sent_time = time.time()
n = sock.sendto(what, destination)
return (n, sent_time)
def receive_udp(sock, destination, expiration=None,
ignore_unexpected=False, one_rr_per_rrset=False,
keyring=None, request_mac=b'', ignore_trailing=False):
"""Read a DNS message from a UDP socket.
*sock*, a ``socket``.
*destination*, a destination tuple appropriate for the address family
of the socket, specifying where the associated query was sent.
*expiration*, a ``float`` or ``None``, the absolute time at which
a timeout exception should be raised. If ``None``, no timeout will
occur.
*ignore_unexpected*, a ``bool``. If ``True``, ignore responses from
unexpected sources.
*one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own
RRset.
*keyring*, a ``dict``, the keyring to use for TSIG.
*request_mac*, a ``binary``, the MAC of the request (for TSIG).
*ignore_trailing*, a ``bool``. If ``True``, ignore trailing
junk at end of the received message.
Raises if the message is malformed, if network errors occur, of if
there is a timeout.
Returns a ``dns.message.Message`` object.
"""
wire = b''
while 1:
_wait_for_readable(sock, expiration)
(wire, from_address) = sock.recvfrom(65535)
if _addresses_equal(sock.family, from_address, destination) or \
(dns.inet.is_multicast(destination[0]) and
from_address[1:] == destination[1:]):
break
if not ignore_unexpected:
raise UnexpectedSource('got a response from '
'%s instead of %s' % (from_address,
destination))
received_time = time.time()
r = dns.message.from_wire(wire, keyring=keyring, request_mac=request_mac,
one_rr_per_rrset=one_rr_per_rrset,
ignore_trailing=ignore_trailing)
return (r, received_time)
def udp(q, where, timeout=None, port=53, af=None, source=None, source_port=0,
ignore_unexpected=False, one_rr_per_rrset=False, ignore_trailing=False):
"""Return the response obtained after sending a query via UDP.
*q*, a ``dns.message.Message``, the query to send
*where*, a ``text`` containing an IPv4 or IPv6 address, where
to send the message.
*timeout*, a ``float`` or ``None``, the number of seconds to wait before the
query times out. If ``None``, the default, wait forever.
*port*, an ``int``, the port send the message to. The default is 53.
*af*, an ``int``, the address family to use. The default is ``None``,
which causes the address family to use to be inferred from the form of
*where*. If the inference attempt fails, AF_INET is used. This
parameter is historical; you need never set it.
*source*, a ``text`` containing an IPv4 or IPv6 address, specifying
the source address. The default is the wildcard address.
*source_port*, an ``int``, the port from which to send the message.
The default is 0.
*ignore_unexpected*, a ``bool``. If ``True``, ignore responses from
unexpected sources.
*one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own
RRset.
*ignore_trailing*, a ``bool``. If ``True``, ignore trailing
junk at end of the received message.
Returns a ``dns.message.Message``.
"""
wire = q.to_wire()
(af, destination, source) = _destination_and_source(af, where, port,
source, source_port)
s = socket_factory(af, socket.SOCK_DGRAM, 0)
received_time = None
sent_time = None
try:
expiration = _compute_expiration(timeout)
s.setblocking(0)
if source is not None:
s.bind(source)
(_, sent_time) = send_udp(s, wire, destination, expiration)
(r, received_time) = receive_udp(s, destination, expiration,
ignore_unexpected, one_rr_per_rrset,
q.keyring, q.mac, ignore_trailing)
finally:
if sent_time is None or received_time is None:
response_time = 0
else:
response_time = received_time - sent_time
s.close()
r.time = response_time
if not q.is_response(r):
raise BadResponse
return r
def _net_read(sock, count, expiration):
"""Read the specified number of bytes from sock. Keep trying until we
either get the desired amount, or we hit EOF.
A Timeout exception will be raised if the operation is not completed
by the expiration time.
"""
s = b''
while count > 0:
_wait_for_readable(sock, expiration)
n = sock.recv(count)
if n == b'':
raise EOFError
count = count - len(n)
s = s + n
return s
def _net_write(sock, data, expiration):
"""Write the specified data to the socket.
A Timeout exception will be raised if the operation is not completed
by the expiration time.
"""
current = 0
l = len(data)
while current < l:
_wait_for_writable(sock, expiration)
current += sock.send(data[current:])
def send_tcp(sock, what, expiration=None):
"""Send a DNS message to the specified TCP socket.
*sock*, a ``socket``.
*what*, a ``binary`` or ``dns.message.Message``, the message to send.
*expiration*, a ``float`` or ``None``, the absolute time at which
a timeout exception should be raised. If ``None``, no timeout will
occur.
Returns an ``(int, float)`` tuple of bytes sent and the sent time.
"""
if isinstance(what, dns.message.Message):
what = what.to_wire()
l = len(what)
# copying the wire into tcpmsg is inefficient, but lets us
# avoid writev() or doing a short write that would get pushed
# onto the net
tcpmsg = struct.pack("!H", l) + what
_wait_for_writable(sock, expiration)
sent_time = time.time()
_net_write(sock, tcpmsg, expiration)
return (len(tcpmsg), sent_time)
def receive_tcp(sock, expiration=None, one_rr_per_rrset=False,
keyring=None, request_mac=b'', ignore_trailing=False):
"""Read a DNS message from a TCP socket.
*sock*, a ``socket``.
*expiration*, a ``float`` or ``None``, the absolute time at which
a timeout exception should be raised. If ``None``, no timeout will
occur.
*one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own
RRset.
*keyring*, a ``dict``, the keyring to use for TSIG.
*request_mac*, a ``binary``, the MAC of the request (for TSIG).
*ignore_trailing*, a ``bool``. If ``True``, ignore trailing
junk at end of the received message.
Raises if the message is malformed, if network errors occur, of if
there is a timeout.
Returns a ``dns.message.Message`` object.
"""
ldata = _net_read(sock, 2, expiration)
(l,) = struct.unpack("!H", ldata)
wire = _net_read(sock, l, expiration)
received_time = time.time()
r = dns.message.from_wire(wire, keyring=keyring, request_mac=request_mac,
one_rr_per_rrset=one_rr_per_rrset,
ignore_trailing=ignore_trailing)
return (r, received_time)
def _connect(s, address):
try:
s.connect(address)
except socket.error:
(ty, v) = sys.exc_info()[:2]
if hasattr(v, 'errno'):
v_err = v.errno
else:
v_err = v[0]
if v_err not in [errno.EINPROGRESS, errno.EWOULDBLOCK, errno.EALREADY]:
raise v
def tcp(q, where, timeout=None, port=53, af=None, source=None, source_port=0,
one_rr_per_rrset=False, ignore_trailing=False):
"""Return the response obtained after sending a query via TCP.
*q*, a ``dns.message.Message``, the query to send
*where*, a ``text`` containing an IPv4 or IPv6 address, where
to send the message.
*timeout*, a ``float`` or ``None``, the number of seconds to wait before the
query times out. If ``None``, the default, wait forever.
*port*, an ``int``, the port send the message to. The default is 53.
*af*, an ``int``, the address family to use. The default is ``None``,
which causes the address family to use to be inferred from the form of
*where*. If the inference attempt fails, AF_INET is used. This
parameter is historical; you need never set it.
*source*, a ``text`` containing an IPv4 or IPv6 address, specifying
the source address. The default is the wildcard address.
*source_port*, an ``int``, the port from which to send the message.
The default is 0.
*one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own
RRset.
*ignore_trailing*, a ``bool``. If ``True``, ignore trailing
junk at end of the received message.
Returns a ``dns.message.Message``.
"""
wire = q.to_wire()
(af, destination, source) = _destination_and_source(af, where, port,
source, source_port)
s = socket_factory(af, socket.SOCK_STREAM, 0)
begin_time = None
received_time = None
try:
expiration = _compute_expiration(timeout)
s.setblocking(0)
begin_time = time.time()
if source is not None:
s.bind(source)
_connect(s, destination)
send_tcp(s, wire, expiration)
(r, received_time) = receive_tcp(s, expiration, one_rr_per_rrset,
q.keyring, q.mac, ignore_trailing)
finally:
if begin_time is None or received_time is None:
response_time = 0
else:
response_time = received_time - begin_time
s.close()
r.time = response_time
if not q.is_response(r):
raise BadResponse
return r
def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN,
timeout=None, port=53, keyring=None, keyname=None, relativize=True,
af=None, lifetime=None, source=None, source_port=0, serial=0,
use_udp=False, keyalgorithm=dns.tsig.default_algorithm):
"""Return a generator for the responses to a zone transfer.
*where*. If the inference attempt fails, AF_INET is used. This
parameter is historical; you need never set it.
*zone*, a ``dns.name.Name`` or ``text``, the name of the zone to transfer.
*rdtype*, an ``int`` or ``text``, the type of zone transfer. The
default is ``dns.rdatatype.AXFR``. ``dns.rdatatype.IXFR`` can be
used to do an incremental transfer instead.
*rdclass*, an ``int`` or ``text``, the class of the zone transfer.
The default is ``dns.rdataclass.IN``.
*timeout*, a ``float``, the number of seconds to wait for each
response message. If None, the default, wait forever.
*port*, an ``int``, the port send the message to. The default is 53.
*keyring*, a ``dict``, the keyring to use for TSIG.
*keyname*, a ``dns.name.Name`` or ``text``, the name of the TSIG
key to use.
*relativize*, a ``bool``. If ``True``, all names in the zone will be
relativized to the zone origin. It is essential that the
relativize setting matches the one specified to
``dns.zone.from_xfr()`` if using this generator to make a zone.
*af*, an ``int``, the address family to use. The default is ``None``,
which causes the address family to use to be inferred from the form of
*where*. If the inference attempt fails, AF_INET is used. This
parameter is historical; you need never set it.
*lifetime*, a ``float``, the total number of seconds to spend
doing the transfer. If ``None``, the default, then there is no
limit on the time the transfer may take.
*source*, a ``text`` containing an IPv4 or IPv6 address, specifying
the source address. The default is the wildcard address.
*source_port*, an ``int``, the port from which to send the message.
The default is 0.
*serial*, an ``int``, the SOA serial number to use as the base for
an IXFR diff sequence (only meaningful if *rdtype* is
``dns.rdatatype.IXFR``).
*use_udp*, a ``bool``. If ``True``, use UDP (only meaningful for IXFR).
*keyalgorithm*, a ``dns.name.Name`` or ``text``, the TSIG algorithm to use.
Raises on errors, and so does the generator.
Returns a generator of ``dns.message.Message`` objects.
"""
if isinstance(zone, string_types):
zone = dns.name.from_text(zone)
if isinstance(rdtype, string_types):
rdtype = dns.rdatatype.from_text(rdtype)
q = dns.message.make_query(zone, rdtype, rdclass)
if rdtype == dns.rdatatype.IXFR:
rrset = dns.rrset.from_text(zone, 0, 'IN', 'SOA',
'. . %u 0 0 0 0' % serial)
q.authority.append(rrset)
if keyring is not None:
q.use_tsig(keyring, keyname, algorithm=keyalgorithm)
wire = q.to_wire()
(af, destination, source) = _destination_and_source(af, where, port,
source, source_port)
if use_udp:
if rdtype != dns.rdatatype.IXFR:
raise ValueError('cannot do a UDP AXFR')
s = socket_factory(af, socket.SOCK_DGRAM, 0)
else:
s = socket_factory(af, socket.SOCK_STREAM, 0)
s.setblocking(0)
if source is not None:
s.bind(source)
expiration = _compute_expiration(lifetime)
_connect(s, destination)
l = len(wire)
if use_udp:
_wait_for_writable(s, expiration)
s.send(wire)
else:
tcpmsg = struct.pack("!H", l) + wire
_net_write(s, tcpmsg, expiration)
done = False
delete_mode = True
expecting_SOA = False
soa_rrset = None
if relativize:
origin = zone
oname = dns.name.empty
else:
origin = None
oname = zone
tsig_ctx = None
first = True
while not done:
mexpiration = _compute_expiration(timeout)
if mexpiration is None or mexpiration > expiration:
mexpiration = expiration
if use_udp:
_wait_for_readable(s, expiration)
(wire, from_address) = s.recvfrom(65535)
else:
ldata = _net_read(s, 2, mexpiration)
(l,) = struct.unpack("!H", ldata)
wire = _net_read(s, l, mexpiration)
is_ixfr = (rdtype == dns.rdatatype.IXFR)
r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac,
xfr=True, origin=origin, tsig_ctx=tsig_ctx,
multi=True, first=first,
one_rr_per_rrset=is_ixfr)
rcode = r.rcode()
if rcode != dns.rcode.NOERROR:
raise TransferError(rcode)
tsig_ctx = r.tsig_ctx
first = False
answer_index = 0
if soa_rrset is None:
if not r.answer or r.answer[0].name != oname:
raise dns.exception.FormError(
"No answer or RRset not for qname")
rrset = r.answer[0]
if rrset.rdtype != dns.rdatatype.SOA:
raise dns.exception.FormError("first RRset is not an SOA")
answer_index = 1
soa_rrset = rrset.copy()
if rdtype == dns.rdatatype.IXFR:
if soa_rrset[0].serial <= serial:
#
# We're already up-to-date.
#
done = True
else:
expecting_SOA = True
#
# Process SOAs in the answer section (other than the initial
# SOA in the first message).
#
for rrset in r.answer[answer_index:]:
if done:
raise dns.exception.FormError("answers after final SOA")
if rrset.rdtype == dns.rdatatype.SOA and rrset.name == oname:
if expecting_SOA:
if rrset[0].serial != serial:
raise dns.exception.FormError(
"IXFR base serial mismatch")
expecting_SOA = False
elif rdtype == dns.rdatatype.IXFR:
delete_mode = not delete_mode
#
# If this SOA RRset is equal to the first we saw then we're
# finished. If this is an IXFR we also check that we're seeing
# the record in the expected part of the response.
#
if rrset == soa_rrset and \
(rdtype == dns.rdatatype.AXFR or
(rdtype == dns.rdatatype.IXFR and delete_mode)):
done = True
elif expecting_SOA:
#
# We made an IXFR request and are expecting another
# SOA RR, but saw something else, so this must be an
# AXFR response.
#
rdtype = dns.rdatatype.AXFR
expecting_SOA = False
if done and q.keyring and not r.had_tsig:
raise dns.exception.FormError("missing TSIG")
yield r
s.close()

15
src/dns/query.pyi Normal file
View File

@@ -0,0 +1,15 @@
from typing import Optional, Union, Dict, Generator, Any
from . import message, tsig, rdatatype, rdataclass, name, message
def tcp(q : message.Message, where : str, timeout : float = None, port=53, af : Optional[int] = None, source : Optional[str] = None, source_port : int = 0,
one_rr_per_rrset=False) -> message.Message:
pass
def xfr(where : None, zone : Union[name.Name,str], rdtype=rdatatype.AXFR, rdclass=rdataclass.IN,
timeout : Optional[float] =None, port=53, keyring : Optional[Dict[name.Name, bytes]] =None, keyname : Union[str,name.Name]=None, relativize=True,
af : Optional[int] =None, lifetime : Optional[float]=None, source : Optional[str] =None, source_port=0, serial=0,
use_udp=False, keyalgorithm=tsig.default_algorithm) -> Generator[Any,Any,message.Message]:
pass
def udp(q : message.Message, where : str, timeout : Optional[float] = None, port=53, af : Optional[int] = None, source : Optional[str] = None, source_port=0,
ignore_unexpected=False, one_rr_per_rrset=False) -> message.Message:
...

144
src/dns/rcode.py Normal file
View File

@@ -0,0 +1,144 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2001-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""DNS Result Codes."""
import dns.exception
from ._compat import long
#: No error
NOERROR = 0
#: Form error
FORMERR = 1
#: Server failure
SERVFAIL = 2
#: Name does not exist ("Name Error" in RFC 1025 terminology).
NXDOMAIN = 3
#: Not implemented
NOTIMP = 4
#: Refused
REFUSED = 5
#: Name exists.
YXDOMAIN = 6
#: RRset exists.
YXRRSET = 7
#: RRset does not exist.
NXRRSET = 8
#: Not authoritative.
NOTAUTH = 9
#: Name not in zone.
NOTZONE = 10
#: Bad EDNS version.
BADVERS = 16
_by_text = {
'NOERROR': NOERROR,
'FORMERR': FORMERR,
'SERVFAIL': SERVFAIL,
'NXDOMAIN': NXDOMAIN,
'NOTIMP': NOTIMP,
'REFUSED': REFUSED,
'YXDOMAIN': YXDOMAIN,
'YXRRSET': YXRRSET,
'NXRRSET': NXRRSET,
'NOTAUTH': NOTAUTH,
'NOTZONE': NOTZONE,
'BADVERS': BADVERS
}
# We construct the inverse mapping programmatically to ensure that we
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
# would cause the mapping not to be a true inverse.
_by_value = {y: x for x, y in _by_text.items()}
class UnknownRcode(dns.exception.DNSException):
"""A DNS rcode is unknown."""
def from_text(text):
"""Convert text into an rcode.
*text*, a ``text``, the textual rcode or an integer in textual form.
Raises ``dns.rcode.UnknownRcode`` if the rcode mnemonic is unknown.
Returns an ``int``.
"""
if text.isdigit():
v = int(text)
if v >= 0 and v <= 4095:
return v
v = _by_text.get(text.upper())
if v is None:
raise UnknownRcode
return v
def from_flags(flags, ednsflags):
"""Return the rcode value encoded by flags and ednsflags.
*flags*, an ``int``, the DNS flags field.
*ednsflags*, an ``int``, the EDNS flags field.
Raises ``ValueError`` if rcode is < 0 or > 4095
Returns an ``int``.
"""
value = (flags & 0x000f) | ((ednsflags >> 20) & 0xff0)
if value < 0 or value > 4095:
raise ValueError('rcode must be >= 0 and <= 4095')
return value
def to_flags(value):
"""Return a (flags, ednsflags) tuple which encodes the rcode.
*value*, an ``int``, the rcode.
Raises ``ValueError`` if rcode is < 0 or > 4095.
Returns an ``(int, int)`` tuple.
"""
if value < 0 or value > 4095:
raise ValueError('rcode must be >= 0 and <= 4095')
v = value & 0xf
ev = long(value & 0xff0) << 20
return (v, ev)
def to_text(value):
"""Convert rcode into text.
*value*, and ``int``, the rcode.
Raises ``ValueError`` if rcode is < 0 or > 4095.
Returns a ``text``.
"""
if value < 0 or value > 4095:
raise ValueError('rcode must be >= 0 and <= 4095')
text = _by_value.get(value)
if text is None:
text = str(value)
return text

456
src/dns/rdata.py Normal file
View File

@@ -0,0 +1,456 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2001-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""DNS rdata."""
from io import BytesIO
import base64
import binascii
import dns.exception
import dns.name
import dns.rdataclass
import dns.rdatatype
import dns.tokenizer
import dns.wiredata
from ._compat import xrange, string_types, text_type
try:
import threading as _threading
except ImportError:
import dummy_threading as _threading
_hex_chunksize = 32
def _hexify(data, chunksize=_hex_chunksize):
"""Convert a binary string into its hex encoding, broken up into chunks
of chunksize characters separated by a space.
"""
line = binascii.hexlify(data)
return b' '.join([line[i:i + chunksize]
for i
in range(0, len(line), chunksize)]).decode()
_base64_chunksize = 32
def _base64ify(data, chunksize=_base64_chunksize):
"""Convert a binary string into its base64 encoding, broken up into chunks
of chunksize characters separated by a space.
"""
line = base64.b64encode(data)
return b' '.join([line[i:i + chunksize]
for i
in range(0, len(line), chunksize)]).decode()
__escaped = bytearray(b'"\\')
def _escapify(qstring):
"""Escape the characters in a quoted string which need it."""
if isinstance(qstring, text_type):
qstring = qstring.encode()
if not isinstance(qstring, bytearray):
qstring = bytearray(qstring)
text = ''
for c in qstring:
if c in __escaped:
text += '\\' + chr(c)
elif c >= 0x20 and c < 0x7F:
text += chr(c)
else:
text += '\\%03d' % c
return text
def _truncate_bitmap(what):
"""Determine the index of greatest byte that isn't all zeros, and
return the bitmap that contains all the bytes less than that index.
"""
for i in xrange(len(what) - 1, -1, -1):
if what[i] != 0:
return what[0: i + 1]
return what[0:1]
class Rdata(object):
"""Base class for all DNS rdata types."""
__slots__ = ['rdclass', 'rdtype']
def __init__(self, rdclass, rdtype):
"""Initialize an rdata.
*rdclass*, an ``int`` is the rdataclass of the Rdata.
*rdtype*, an ``int`` is the rdatatype of the Rdata.
"""
self.rdclass = rdclass
self.rdtype = rdtype
def covers(self):
"""Return the type a Rdata covers.
DNS SIG/RRSIG rdatas apply to a specific type; this type is
returned by the covers() function. If the rdata type is not
SIG or RRSIG, dns.rdatatype.NONE is returned. This is useful when
creating rdatasets, allowing the rdataset to contain only RRSIGs
of a particular type, e.g. RRSIG(NS).
Returns an ``int``.
"""
return dns.rdatatype.NONE
def extended_rdatatype(self):
"""Return a 32-bit type value, the least significant 16 bits of
which are the ordinary DNS type, and the upper 16 bits of which are
the "covered" type, if any.
Returns an ``int``.
"""
return self.covers() << 16 | self.rdtype
def to_text(self, origin=None, relativize=True, **kw):
"""Convert an rdata to text format.
Returns a ``text``.
"""
raise NotImplementedError
def to_wire(self, file, compress=None, origin=None):
"""Convert an rdata to wire format.
Returns a ``binary``.
"""
raise NotImplementedError
def to_digestable(self, origin=None):
"""Convert rdata to a format suitable for digesting in hashes. This
is also the DNSSEC canonical form.
Returns a ``binary``.
"""
f = BytesIO()
self.to_wire(f, None, origin)
return f.getvalue()
def validate(self):
"""Check that the current contents of the rdata's fields are
valid.
If you change an rdata by assigning to its fields,
it is a good idea to call validate() when you are done making
changes.
Raises various exceptions if there are problems.
Returns ``None``.
"""
dns.rdata.from_text(self.rdclass, self.rdtype, self.to_text())
def __repr__(self):
covers = self.covers()
if covers == dns.rdatatype.NONE:
ctext = ''
else:
ctext = '(' + dns.rdatatype.to_text(covers) + ')'
return '<DNS ' + dns.rdataclass.to_text(self.rdclass) + ' ' + \
dns.rdatatype.to_text(self.rdtype) + ctext + ' rdata: ' + \
str(self) + '>'
def __str__(self):
return self.to_text()
def _cmp(self, other):
"""Compare an rdata with another rdata of the same rdtype and
rdclass.
Return < 0 if self < other in the DNSSEC ordering, 0 if self
== other, and > 0 if self > other.
"""
our = self.to_digestable(dns.name.root)
their = other.to_digestable(dns.name.root)
if our == their:
return 0
elif our > their:
return 1
else:
return -1
def __eq__(self, other):
if not isinstance(other, Rdata):
return False
if self.rdclass != other.rdclass or self.rdtype != other.rdtype:
return False
return self._cmp(other) == 0
def __ne__(self, other):
if not isinstance(other, Rdata):
return True
if self.rdclass != other.rdclass or self.rdtype != other.rdtype:
return True
return self._cmp(other) != 0
def __lt__(self, other):
if not isinstance(other, Rdata) or \
self.rdclass != other.rdclass or self.rdtype != other.rdtype:
return NotImplemented
return self._cmp(other) < 0
def __le__(self, other):
if not isinstance(other, Rdata) or \
self.rdclass != other.rdclass or self.rdtype != other.rdtype:
return NotImplemented
return self._cmp(other) <= 0
def __ge__(self, other):
if not isinstance(other, Rdata) or \
self.rdclass != other.rdclass or self.rdtype != other.rdtype:
return NotImplemented
return self._cmp(other) >= 0
def __gt__(self, other):
if not isinstance(other, Rdata) or \
self.rdclass != other.rdclass or self.rdtype != other.rdtype:
return NotImplemented
return self._cmp(other) > 0
def __hash__(self):
return hash(self.to_digestable(dns.name.root))
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
raise NotImplementedError
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
raise NotImplementedError
def choose_relativity(self, origin=None, relativize=True):
"""Convert any domain names in the rdata to the specified
relativization.
"""
class GenericRdata(Rdata):
"""Generic Rdata Class
This class is used for rdata types for which we have no better
implementation. It implements the DNS "unknown RRs" scheme.
"""
__slots__ = ['data']
def __init__(self, rdclass, rdtype, data):
super(GenericRdata, self).__init__(rdclass, rdtype)
self.data = data
def to_text(self, origin=None, relativize=True, **kw):
return r'\# %d ' % len(self.data) + _hexify(self.data)
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
token = tok.get()
if not token.is_identifier() or token.value != r'\#':
raise dns.exception.SyntaxError(
r'generic rdata does not start with \#')
length = tok.get_int()
chunks = []
while 1:
token = tok.get()
if token.is_eol_or_eof():
break
chunks.append(token.value.encode())
hex = b''.join(chunks)
data = binascii.unhexlify(hex)
if len(data) != length:
raise dns.exception.SyntaxError(
'generic rdata hex data has wrong length')
return cls(rdclass, rdtype, data)
def to_wire(self, file, compress=None, origin=None):
file.write(self.data)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
return cls(rdclass, rdtype, wire[current: current + rdlen])
_rdata_modules = {}
_module_prefix = 'dns.rdtypes'
_import_lock = _threading.Lock()
def get_rdata_class(rdclass, rdtype):
def import_module(name):
with _import_lock:
mod = __import__(name)
components = name.split('.')
for comp in components[1:]:
mod = getattr(mod, comp)
return mod
mod = _rdata_modules.get((rdclass, rdtype))
rdclass_text = dns.rdataclass.to_text(rdclass)
rdtype_text = dns.rdatatype.to_text(rdtype)
rdtype_text = rdtype_text.replace('-', '_')
if not mod:
mod = _rdata_modules.get((dns.rdatatype.ANY, rdtype))
if not mod:
try:
mod = import_module('.'.join([_module_prefix,
rdclass_text, rdtype_text]))
_rdata_modules[(rdclass, rdtype)] = mod
except ImportError:
try:
mod = import_module('.'.join([_module_prefix,
'ANY', rdtype_text]))
_rdata_modules[(dns.rdataclass.ANY, rdtype)] = mod
except ImportError:
mod = None
if mod:
cls = getattr(mod, rdtype_text)
else:
cls = GenericRdata
return cls
def from_text(rdclass, rdtype, tok, origin=None, relativize=True):
"""Build an rdata object from text format.
This function attempts to dynamically load a class which
implements the specified rdata class and type. If there is no
class-and-type-specific implementation, the GenericRdata class
is used.
Once a class is chosen, its from_text() class method is called
with the parameters to this function.
If *tok* is a ``text``, then a tokenizer is created and the string
is used as its input.
*rdclass*, an ``int``, the rdataclass.
*rdtype*, an ``int``, the rdatatype.
*tok*, a ``dns.tokenizer.Tokenizer`` or a ``text``.
*origin*, a ``dns.name.Name`` (or ``None``), the
origin to use for relative names.
*relativize*, a ``bool``. If true, name will be relativized to
the specified origin.
Returns an instance of the chosen Rdata subclass.
"""
if isinstance(tok, string_types):
tok = dns.tokenizer.Tokenizer(tok)
cls = get_rdata_class(rdclass, rdtype)
if cls != GenericRdata:
# peek at first token
token = tok.get()
tok.unget(token)
if token.is_identifier() and \
token.value == r'\#':
#
# Known type using the generic syntax. Extract the
# wire form from the generic syntax, and then run
# from_wire on it.
#
rdata = GenericRdata.from_text(rdclass, rdtype, tok, origin,
relativize)
return from_wire(rdclass, rdtype, rdata.data, 0, len(rdata.data),
origin)
return cls.from_text(rdclass, rdtype, tok, origin, relativize)
def from_wire(rdclass, rdtype, wire, current, rdlen, origin=None):
"""Build an rdata object from wire format
This function attempts to dynamically load a class which
implements the specified rdata class and type. If there is no
class-and-type-specific implementation, the GenericRdata class
is used.
Once a class is chosen, its from_wire() class method is called
with the parameters to this function.
*rdclass*, an ``int``, the rdataclass.
*rdtype*, an ``int``, the rdatatype.
*wire*, a ``binary``, the wire-format message.
*current*, an ``int``, the offset in wire of the beginning of
the rdata.
*rdlen*, an ``int``, the length of the wire-format rdata
*origin*, a ``dns.name.Name`` (or ``None``). If not ``None``,
then names will be relativized to this origin.
Returns an instance of the chosen Rdata subclass.
"""
wire = dns.wiredata.maybe_wrap(wire)
cls = get_rdata_class(rdclass, rdtype)
return cls.from_wire(rdclass, rdtype, wire, current, rdlen, origin)
class RdatatypeExists(dns.exception.DNSException):
"""DNS rdatatype already exists."""
supp_kwargs = {'rdclass', 'rdtype'}
fmt = "The rdata type with class {rdclass} and rdtype {rdtype} " + \
"already exists."
def register_type(implementation, rdtype, rdtype_text, is_singleton=False,
rdclass=dns.rdataclass.IN):
"""Dynamically register a module to handle an rdatatype.
*implementation*, a module implementing the type in the usual dnspython
way.
*rdtype*, an ``int``, the rdatatype to register.
*rdtype_text*, a ``text``, the textual form of the rdatatype.
*is_singleton*, a ``bool``, indicating if the type is a singleton (i.e.
RRsets of the type can have only one member.)
*rdclass*, the rdataclass of the type, or ``dns.rdataclass.ANY`` if
it applies to all classes.
"""
existing_cls = get_rdata_class(rdclass, rdtype)
if existing_cls != GenericRdata:
raise RdatatypeExists(rdclass=rdclass, rdtype=rdtype)
_rdata_modules[(rdclass, rdtype)] = implementation
dns.rdatatype.register_type(rdtype, rdtype_text, is_singleton)

17
src/dns/rdata.pyi Normal file
View File

@@ -0,0 +1,17 @@
from typing import Dict, Tuple, Any, Optional
from .name import Name
class Rdata:
def __init__(self):
self.address : str
def to_wire(self, file, compress : Optional[Dict[Name,int]], origin : Optional[Name]) -> bytes:
...
@classmethod
def from_text(cls, rdclass : int, rdtype : int, tok, origin=None, relativize=True):
...
_rdata_modules : Dict[Tuple[Any,Rdata],Any]
def from_text(rdclass : int, rdtype : int, tok : Optional[str], origin : Optional[Name] = None, relativize : bool = True):
...
def from_wire(rdclass : int, rdtype : int, wire : bytes, current : int, rdlen : int, origin : Optional[Name] = None):
...

122
src/dns/rdataclass.py Normal file
View File

@@ -0,0 +1,122 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2001-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""DNS Rdata Classes."""
import re
import dns.exception
RESERVED0 = 0
IN = 1
CH = 3
HS = 4
NONE = 254
ANY = 255
_by_text = {
'RESERVED0': RESERVED0,
'IN': IN,
'CH': CH,
'HS': HS,
'NONE': NONE,
'ANY': ANY
}
# We construct the inverse mapping programmatically to ensure that we
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
# would cause the mapping not to be true inverse.
_by_value = {y: x for x, y in _by_text.items()}
# Now that we've built the inverse map, we can add class aliases to
# the _by_text mapping.
_by_text.update({
'INTERNET': IN,
'CHAOS': CH,
'HESIOD': HS
})
_metaclasses = {
NONE: True,
ANY: True
}
_unknown_class_pattern = re.compile('CLASS([0-9]+)$', re.I)
class UnknownRdataclass(dns.exception.DNSException):
"""A DNS class is unknown."""
def from_text(text):
"""Convert text into a DNS rdata class value.
The input text can be a defined DNS RR class mnemonic or
instance of the DNS generic class syntax.
For example, "IN" and "CLASS1" will both result in a value of 1.
Raises ``dns.rdatatype.UnknownRdataclass`` if the class is unknown.
Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535.
Returns an ``int``.
"""
value = _by_text.get(text.upper())
if value is None:
match = _unknown_class_pattern.match(text)
if match is None:
raise UnknownRdataclass
value = int(match.group(1))
if value < 0 or value > 65535:
raise ValueError("class must be between >= 0 and <= 65535")
return value
def to_text(value):
"""Convert a DNS rdata type value to text.
If the value has a known mnemonic, it will be used, otherwise the
DNS generic class syntax will be used.
Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535.
Returns a ``str``.
"""
if value < 0 or value > 65535:
raise ValueError("class must be between >= 0 and <= 65535")
text = _by_value.get(value)
if text is None:
text = 'CLASS' + repr(value)
return text
def is_metaclass(rdclass):
"""True if the specified class is a metaclass.
The currently defined metaclasses are ANY and NONE.
*rdclass* is an ``int``.
"""
if rdclass in _metaclasses:
return True
return False

347
src/dns/rdataset.py Normal file
View File

@@ -0,0 +1,347 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2001-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""DNS rdatasets (an rdataset is a set of rdatas of a given type and class)"""
import random
from io import StringIO
import struct
import dns.exception
import dns.rdatatype
import dns.rdataclass
import dns.rdata
import dns.set
from ._compat import string_types
# define SimpleSet here for backwards compatibility
SimpleSet = dns.set.Set
class DifferingCovers(dns.exception.DNSException):
"""An attempt was made to add a DNS SIG/RRSIG whose covered type
is not the same as that of the other rdatas in the rdataset."""
class IncompatibleTypes(dns.exception.DNSException):
"""An attempt was made to add DNS RR data of an incompatible type."""
class Rdataset(dns.set.Set):
"""A DNS rdataset."""
__slots__ = ['rdclass', 'rdtype', 'covers', 'ttl']
def __init__(self, rdclass, rdtype, covers=dns.rdatatype.NONE, ttl=0):
"""Create a new rdataset of the specified class and type.
*rdclass*, an ``int``, the rdataclass.
*rdtype*, an ``int``, the rdatatype.
*covers*, an ``int``, the covered rdatatype.
*ttl*, an ``int``, the TTL.
"""
super(Rdataset, self).__init__()
self.rdclass = rdclass
self.rdtype = rdtype
self.covers = covers
self.ttl = ttl
def _clone(self):
obj = super(Rdataset, self)._clone()
obj.rdclass = self.rdclass
obj.rdtype = self.rdtype
obj.covers = self.covers
obj.ttl = self.ttl
return obj
def update_ttl(self, ttl):
"""Perform TTL minimization.
Set the TTL of the rdataset to be the lesser of the set's current
TTL or the specified TTL. If the set contains no rdatas, set the TTL
to the specified TTL.
*ttl*, an ``int``.
"""
if len(self) == 0:
self.ttl = ttl
elif ttl < self.ttl:
self.ttl = ttl
def add(self, rd, ttl=None):
"""Add the specified rdata to the rdataset.
If the optional *ttl* parameter is supplied, then
``self.update_ttl(ttl)`` will be called prior to adding the rdata.
*rd*, a ``dns.rdata.Rdata``, the rdata
*ttl*, an ``int``, the TTL.
Raises ``dns.rdataset.IncompatibleTypes`` if the type and class
do not match the type and class of the rdataset.
Raises ``dns.rdataset.DifferingCovers`` if the type is a signature
type and the covered type does not match that of the rdataset.
"""
#
# If we're adding a signature, do some special handling to
# check that the signature covers the same type as the
# other rdatas in this rdataset. If this is the first rdata
# in the set, initialize the covers field.
#
if self.rdclass != rd.rdclass or self.rdtype != rd.rdtype:
raise IncompatibleTypes
if ttl is not None:
self.update_ttl(ttl)
if self.rdtype == dns.rdatatype.RRSIG or \
self.rdtype == dns.rdatatype.SIG:
covers = rd.covers()
if len(self) == 0 and self.covers == dns.rdatatype.NONE:
self.covers = covers
elif self.covers != covers:
raise DifferingCovers
if dns.rdatatype.is_singleton(rd.rdtype) and len(self) > 0:
self.clear()
super(Rdataset, self).add(rd)
def union_update(self, other):
self.update_ttl(other.ttl)
super(Rdataset, self).union_update(other)
def intersection_update(self, other):
self.update_ttl(other.ttl)
super(Rdataset, self).intersection_update(other)
def update(self, other):
"""Add all rdatas in other to self.
*other*, a ``dns.rdataset.Rdataset``, the rdataset from which
to update.
"""
self.update_ttl(other.ttl)
super(Rdataset, self).update(other)
def __repr__(self):
if self.covers == 0:
ctext = ''
else:
ctext = '(' + dns.rdatatype.to_text(self.covers) + ')'
return '<DNS ' + dns.rdataclass.to_text(self.rdclass) + ' ' + \
dns.rdatatype.to_text(self.rdtype) + ctext + ' rdataset>'
def __str__(self):
return self.to_text()
def __eq__(self, other):
if not isinstance(other, Rdataset):
return False
if self.rdclass != other.rdclass or \
self.rdtype != other.rdtype or \
self.covers != other.covers:
return False
return super(Rdataset, self).__eq__(other)
def __ne__(self, other):
return not self.__eq__(other)
def to_text(self, name=None, origin=None, relativize=True,
override_rdclass=None, **kw):
"""Convert the rdataset into DNS master file format.
See ``dns.name.Name.choose_relativity`` for more information
on how *origin* and *relativize* determine the way names
are emitted.
Any additional keyword arguments are passed on to the rdata
``to_text()`` method.
*name*, a ``dns.name.Name``. If name is not ``None``, emit RRs with
*name* as the owner name.
*origin*, a ``dns.name.Name`` or ``None``, the origin for relative
names.
*relativize*, a ``bool``. If ``True``, names will be relativized
to *origin*.
"""
if name is not None:
name = name.choose_relativity(origin, relativize)
ntext = str(name)
pad = ' '
else:
ntext = ''
pad = ''
s = StringIO()
if override_rdclass is not None:
rdclass = override_rdclass
else:
rdclass = self.rdclass
if len(self) == 0:
#
# Empty rdatasets are used for the question section, and in
# some dynamic updates, so we don't need to print out the TTL
# (which is meaningless anyway).
#
s.write(u'{}{}{} {}\n'.format(ntext, pad,
dns.rdataclass.to_text(rdclass),
dns.rdatatype.to_text(self.rdtype)))
else:
for rd in self:
s.write(u'%s%s%d %s %s %s\n' %
(ntext, pad, self.ttl, dns.rdataclass.to_text(rdclass),
dns.rdatatype.to_text(self.rdtype),
rd.to_text(origin=origin, relativize=relativize,
**kw)))
#
# We strip off the final \n for the caller's convenience in printing
#
return s.getvalue()[:-1]
def to_wire(self, name, file, compress=None, origin=None,
override_rdclass=None, want_shuffle=True):
"""Convert the rdataset to wire format.
*name*, a ``dns.name.Name`` is the owner name to use.
*file* is the file where the name is emitted (typically a
BytesIO file).
*compress*, a ``dict``, is the compression table to use. If
``None`` (the default), names will not be compressed.
*origin* is a ``dns.name.Name`` or ``None``. If the name is
relative and origin is not ``None``, then *origin* will be appended
to it.
*override_rdclass*, an ``int``, is used as the class instead of the
class of the rdataset. This is useful when rendering rdatasets
associated with dynamic updates.
*want_shuffle*, a ``bool``. If ``True``, then the order of the
Rdatas within the Rdataset will be shuffled before rendering.
Returns an ``int``, the number of records emitted.
"""
if override_rdclass is not None:
rdclass = override_rdclass
want_shuffle = False
else:
rdclass = self.rdclass
file.seek(0, 2)
if len(self) == 0:
name.to_wire(file, compress, origin)
stuff = struct.pack("!HHIH", self.rdtype, rdclass, 0, 0)
file.write(stuff)
return 1
else:
if want_shuffle:
l = list(self)
random.shuffle(l)
else:
l = self
for rd in l:
name.to_wire(file, compress, origin)
stuff = struct.pack("!HHIH", self.rdtype, rdclass,
self.ttl, 0)
file.write(stuff)
start = file.tell()
rd.to_wire(file, compress, origin)
end = file.tell()
assert end - start < 65536
file.seek(start - 2)
stuff = struct.pack("!H", end - start)
file.write(stuff)
file.seek(0, 2)
return len(self)
def match(self, rdclass, rdtype, covers):
"""Returns ``True`` if this rdataset matches the specified class,
type, and covers.
"""
if self.rdclass == rdclass and \
self.rdtype == rdtype and \
self.covers == covers:
return True
return False
def from_text_list(rdclass, rdtype, ttl, text_rdatas):
"""Create an rdataset with the specified class, type, and TTL, and with
the specified list of rdatas in text format.
Returns a ``dns.rdataset.Rdataset`` object.
"""
if isinstance(rdclass, string_types):
rdclass = dns.rdataclass.from_text(rdclass)
if isinstance(rdtype, string_types):
rdtype = dns.rdatatype.from_text(rdtype)
r = Rdataset(rdclass, rdtype)
r.update_ttl(ttl)
for t in text_rdatas:
rd = dns.rdata.from_text(r.rdclass, r.rdtype, t)
r.add(rd)
return r
def from_text(rdclass, rdtype, ttl, *text_rdatas):
"""Create an rdataset with the specified class, type, and TTL, and with
the specified rdatas in text format.
Returns a ``dns.rdataset.Rdataset`` object.
"""
return from_text_list(rdclass, rdtype, ttl, text_rdatas)
def from_rdata_list(ttl, rdatas):
"""Create an rdataset with the specified TTL, and with
the specified list of rdata objects.
Returns a ``dns.rdataset.Rdataset`` object.
"""
if len(rdatas) == 0:
raise ValueError("rdata list must not be empty")
r = None
for rd in rdatas:
if r is None:
r = Rdataset(rd.rdclass, rd.rdtype)
r.update_ttl(ttl)
r.add(rd)
return r
def from_rdata(ttl, *rdatas):
"""Create an rdataset with the specified TTL, and with
the specified rdata objects.
Returns a ``dns.rdataset.Rdataset`` object.
"""
return from_rdata_list(ttl, rdatas)

58
src/dns/rdataset.pyi Normal file
View File

@@ -0,0 +1,58 @@
from typing import Optional, Dict, List, Union
from io import BytesIO
from . import exception, name, set, rdatatype, rdata, rdataset
class DifferingCovers(exception.DNSException):
"""An attempt was made to add a DNS SIG/RRSIG whose covered type
is not the same as that of the other rdatas in the rdataset."""
class IncompatibleTypes(exception.DNSException):
"""An attempt was made to add DNS RR data of an incompatible type."""
class Rdataset(set.Set):
def __init__(self, rdclass, rdtype, covers=rdatatype.NONE, ttl=0):
self.rdclass : int = rdclass
self.rdtype : int = rdtype
self.covers : int = covers
self.ttl : int = ttl
def update_ttl(self, ttl : int) -> None:
...
def add(self, rd : rdata.Rdata, ttl : Optional[int] =None):
...
def union_update(self, other : Rdataset):
...
def intersection_update(self, other : Rdataset):
...
def update(self, other : Rdataset):
...
def to_text(self, name : Optional[name.Name] =None, origin : Optional[name.Name] =None, relativize=True,
override_rdclass : Optional[int] =None, **kw) -> bytes:
...
def to_wire(self, name : Optional[name.Name], file : BytesIO, compress : Optional[Dict[name.Name, int]] = None, origin : Optional[name.Name] = None,
override_rdclass : Optional[int] = None, want_shuffle=True) -> int:
...
def match(self, rdclass : int, rdtype : int, covers : int) -> bool:
...
def from_text_list(rdclass : Union[int,str], rdtype : Union[int,str], ttl : int, text_rdatas : str) -> rdataset.Rdataset:
...
def from_text(rdclass : Union[int,str], rdtype : Union[int,str], ttl : int, *text_rdatas : str) -> rdataset.Rdataset:
...
def from_rdata_list(ttl : int, rdatas : List[rdata.Rdata]) -> rdataset.Rdataset:
...
def from_rdata(ttl : int, *rdatas : List[rdata.Rdata]) -> rdataset.Rdataset:
...

287
src/dns/rdatatype.py Normal file
View File

@@ -0,0 +1,287 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2001-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""DNS Rdata Types."""
import re
import dns.exception
NONE = 0
A = 1
NS = 2
MD = 3
MF = 4
CNAME = 5
SOA = 6
MB = 7
MG = 8
MR = 9
NULL = 10
WKS = 11
PTR = 12
HINFO = 13
MINFO = 14
MX = 15
TXT = 16
RP = 17
AFSDB = 18
X25 = 19
ISDN = 20
RT = 21
NSAP = 22
NSAP_PTR = 23
SIG = 24
KEY = 25
PX = 26
GPOS = 27
AAAA = 28
LOC = 29
NXT = 30
SRV = 33
NAPTR = 35
KX = 36
CERT = 37
A6 = 38
DNAME = 39
OPT = 41
APL = 42
DS = 43
SSHFP = 44
IPSECKEY = 45
RRSIG = 46
NSEC = 47
DNSKEY = 48
DHCID = 49
NSEC3 = 50
NSEC3PARAM = 51
TLSA = 52
HIP = 55
CDS = 59
CDNSKEY = 60
OPENPGPKEY = 61
CSYNC = 62
SPF = 99
UNSPEC = 103
EUI48 = 108
EUI64 = 109
TKEY = 249
TSIG = 250
IXFR = 251
AXFR = 252
MAILB = 253
MAILA = 254
ANY = 255
URI = 256
CAA = 257
AVC = 258
TA = 32768
DLV = 32769
_by_text = {
'NONE': NONE,
'A': A,
'NS': NS,
'MD': MD,
'MF': MF,
'CNAME': CNAME,
'SOA': SOA,
'MB': MB,
'MG': MG,
'MR': MR,
'NULL': NULL,
'WKS': WKS,
'PTR': PTR,
'HINFO': HINFO,
'MINFO': MINFO,
'MX': MX,
'TXT': TXT,
'RP': RP,
'AFSDB': AFSDB,
'X25': X25,
'ISDN': ISDN,
'RT': RT,
'NSAP': NSAP,
'NSAP-PTR': NSAP_PTR,
'SIG': SIG,
'KEY': KEY,
'PX': PX,
'GPOS': GPOS,
'AAAA': AAAA,
'LOC': LOC,
'NXT': NXT,
'SRV': SRV,
'NAPTR': NAPTR,
'KX': KX,
'CERT': CERT,
'A6': A6,
'DNAME': DNAME,
'OPT': OPT,
'APL': APL,
'DS': DS,
'SSHFP': SSHFP,
'IPSECKEY': IPSECKEY,
'RRSIG': RRSIG,
'NSEC': NSEC,
'DNSKEY': DNSKEY,
'DHCID': DHCID,
'NSEC3': NSEC3,
'NSEC3PARAM': NSEC3PARAM,
'TLSA': TLSA,
'HIP': HIP,
'CDS': CDS,
'CDNSKEY': CDNSKEY,
'OPENPGPKEY': OPENPGPKEY,
'CSYNC': CSYNC,
'SPF': SPF,
'UNSPEC': UNSPEC,
'EUI48': EUI48,
'EUI64': EUI64,
'TKEY': TKEY,
'TSIG': TSIG,
'IXFR': IXFR,
'AXFR': AXFR,
'MAILB': MAILB,
'MAILA': MAILA,
'ANY': ANY,
'URI': URI,
'CAA': CAA,
'AVC': AVC,
'TA': TA,
'DLV': DLV,
}
# We construct the inverse mapping programmatically to ensure that we
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
# would cause the mapping not to be true inverse.
_by_value = {y: x for x, y in _by_text.items()}
_metatypes = {
OPT: True
}
_singletons = {
SOA: True,
NXT: True,
DNAME: True,
NSEC: True,
CNAME: True,
}
_unknown_type_pattern = re.compile('TYPE([0-9]+)$', re.I)
class UnknownRdatatype(dns.exception.DNSException):
"""DNS resource record type is unknown."""
def from_text(text):
"""Convert text into a DNS rdata type value.
The input text can be a defined DNS RR type mnemonic or
instance of the DNS generic type syntax.
For example, "NS" and "TYPE2" will both result in a value of 2.
Raises ``dns.rdatatype.UnknownRdatatype`` if the type is unknown.
Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535.
Returns an ``int``.
"""
value = _by_text.get(text.upper())
if value is None:
match = _unknown_type_pattern.match(text)
if match is None:
raise UnknownRdatatype
value = int(match.group(1))
if value < 0 or value > 65535:
raise ValueError("type must be between >= 0 and <= 65535")
return value
def to_text(value):
"""Convert a DNS rdata type value to text.
If the value has a known mnemonic, it will be used, otherwise the
DNS generic type syntax will be used.
Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535.
Returns a ``str``.
"""
if value < 0 or value > 65535:
raise ValueError("type must be between >= 0 and <= 65535")
text = _by_value.get(value)
if text is None:
text = 'TYPE' + repr(value)
return text
def is_metatype(rdtype):
"""True if the specified type is a metatype.
*rdtype* is an ``int``.
The currently defined metatypes are TKEY, TSIG, IXFR, AXFR, MAILA,
MAILB, ANY, and OPT.
Returns a ``bool``.
"""
if rdtype >= TKEY and rdtype <= ANY or rdtype in _metatypes:
return True
return False
def is_singleton(rdtype):
"""Is the specified type a singleton type?
Singleton types can only have a single rdata in an rdataset, or a single
RR in an RRset.
The currently defined singleton types are CNAME, DNAME, NSEC, NXT, and
SOA.
*rdtype* is an ``int``.
Returns a ``bool``.
"""
if rdtype in _singletons:
return True
return False
def register_type(rdtype, rdtype_text, is_singleton=False): # pylint: disable=redefined-outer-name
"""Dynamically register an rdatatype.
*rdtype*, an ``int``, the rdatatype to register.
*rdtype_text*, a ``text``, the textual form of the rdatatype.
*is_singleton*, a ``bool``, indicating if the type is a singleton (i.e.
RRsets of the type can have only one member.)
"""
_by_text[rdtype_text] = rdtype
_by_value[rdtype] = rdtype_text
if is_singleton:
_singletons[rdtype] = True

View File

@@ -0,0 +1,55 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.mxbase
class AFSDB(dns.rdtypes.mxbase.UncompressedDowncasingMX):
"""AFSDB record
@ivar subtype: the subtype value
@type subtype: int
@ivar hostname: the hostname name
@type hostname: dns.name.Name object"""
# Use the property mechanism to make "subtype" an alias for the
# "preference" attribute, and "hostname" an alias for the "exchange"
# attribute.
#
# This lets us inherit the UncompressedMX implementation but lets
# the caller use appropriate attribute names for the rdata type.
#
# We probably lose some performance vs. a cut-and-paste
# implementation, but this way we don't copy code, and that's
# good.
def get_subtype(self):
return self.preference
def set_subtype(self, subtype):
self.preference = subtype
subtype = property(get_subtype, set_subtype)
def get_hostname(self):
return self.exchange
def set_hostname(self, hostname):
self.exchange = hostname
hostname = property(get_hostname, set_hostname)

View File

@@ -0,0 +1,25 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2016 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.txtbase
class AVC(dns.rdtypes.txtbase.TXTBase):
"""AVC record
@see: U{http://www.iana.org/assignments/dns-parameters/AVC/avc-completed-template}"""

View File

@@ -0,0 +1,75 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import dns.exception
import dns.rdata
import dns.tokenizer
class CAA(dns.rdata.Rdata):
"""CAA (Certification Authority Authorization) record
@ivar flags: the flags
@type flags: int
@ivar tag: the tag
@type tag: string
@ivar value: the value
@type value: string
@see: RFC 6844"""
__slots__ = ['flags', 'tag', 'value']
def __init__(self, rdclass, rdtype, flags, tag, value):
super(CAA, self).__init__(rdclass, rdtype)
self.flags = flags
self.tag = tag
self.value = value
def to_text(self, origin=None, relativize=True, **kw):
return '%u %s "%s"' % (self.flags,
dns.rdata._escapify(self.tag),
dns.rdata._escapify(self.value))
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
flags = tok.get_uint8()
tag = tok.get_string().encode()
if len(tag) > 255:
raise dns.exception.SyntaxError("tag too long")
if not tag.isalnum():
raise dns.exception.SyntaxError("tag is not alphanumeric")
value = tok.get_string().encode()
return cls(rdclass, rdtype, flags, tag, value)
def to_wire(self, file, compress=None, origin=None):
file.write(struct.pack('!B', self.flags))
l = len(self.tag)
assert l < 256
file.write(struct.pack('!B', l))
file.write(self.tag)
file.write(self.value)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
(flags, l) = struct.unpack('!BB', wire[current: current + 2])
current += 2
tag = wire[current: current + l]
value = wire[current + l:current + rdlen - 2]
return cls(rdclass, rdtype, flags, tag, value)

View File

@@ -0,0 +1,27 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.dnskeybase
from dns.rdtypes.dnskeybase import flags_to_text_set, flags_from_text_set
__all__ = ['flags_to_text_set', 'flags_from_text_set']
class CDNSKEY(dns.rdtypes.dnskeybase.DNSKEYBase):
"""CDNSKEY record"""

View File

@@ -0,0 +1,23 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.dsbase
class CDS(dns.rdtypes.dsbase.DSBase):
"""CDS record"""

123
src/dns/rdtypes/ANY/CERT.py Normal file
View File

@@ -0,0 +1,123 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import base64
import dns.exception
import dns.dnssec
import dns.rdata
import dns.tokenizer
_ctype_by_value = {
1: 'PKIX',
2: 'SPKI',
3: 'PGP',
253: 'URI',
254: 'OID',
}
_ctype_by_name = {
'PKIX': 1,
'SPKI': 2,
'PGP': 3,
'URI': 253,
'OID': 254,
}
def _ctype_from_text(what):
v = _ctype_by_name.get(what)
if v is not None:
return v
return int(what)
def _ctype_to_text(what):
v = _ctype_by_value.get(what)
if v is not None:
return v
return str(what)
class CERT(dns.rdata.Rdata):
"""CERT record
@ivar certificate_type: certificate type
@type certificate_type: int
@ivar key_tag: key tag
@type key_tag: int
@ivar algorithm: algorithm
@type algorithm: int
@ivar certificate: the certificate or CRL
@type certificate: string
@see: RFC 2538"""
__slots__ = ['certificate_type', 'key_tag', 'algorithm', 'certificate']
def __init__(self, rdclass, rdtype, certificate_type, key_tag, algorithm,
certificate):
super(CERT, self).__init__(rdclass, rdtype)
self.certificate_type = certificate_type
self.key_tag = key_tag
self.algorithm = algorithm
self.certificate = certificate
def to_text(self, origin=None, relativize=True, **kw):
certificate_type = _ctype_to_text(self.certificate_type)
return "%s %d %s %s" % (certificate_type, self.key_tag,
dns.dnssec.algorithm_to_text(self.algorithm),
dns.rdata._base64ify(self.certificate))
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
certificate_type = _ctype_from_text(tok.get_string())
key_tag = tok.get_uint16()
algorithm = dns.dnssec.algorithm_from_text(tok.get_string())
if algorithm < 0 or algorithm > 255:
raise dns.exception.SyntaxError("bad algorithm type")
chunks = []
while 1:
t = tok.get().unescape()
if t.is_eol_or_eof():
break
if not t.is_identifier():
raise dns.exception.SyntaxError
chunks.append(t.value.encode())
b64 = b''.join(chunks)
certificate = base64.b64decode(b64)
return cls(rdclass, rdtype, certificate_type, key_tag,
algorithm, certificate)
def to_wire(self, file, compress=None, origin=None):
prefix = struct.pack("!HHB", self.certificate_type, self.key_tag,
self.algorithm)
file.write(prefix)
file.write(self.certificate)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
prefix = wire[current: current + 5].unwrap()
current += 5
rdlen -= 5
if rdlen < 0:
raise dns.exception.FormError
(certificate_type, key_tag, algorithm) = struct.unpack("!HHB", prefix)
certificate = wire[current: current + rdlen].unwrap()
return cls(rdclass, rdtype, certificate_type, key_tag, algorithm,
certificate)

View File

@@ -0,0 +1,27 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.nsbase
class CNAME(dns.rdtypes.nsbase.NSBase):
"""CNAME record
Note: although CNAME is officially a singleton type, dnspython allows
non-singleton CNAME rdatasets because such sets have been commonly
used by BIND and other nameservers for load balancing."""

View File

@@ -0,0 +1,126 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2004-2007, 2009-2011, 2016 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import dns.exception
import dns.rdata
import dns.rdatatype
import dns.name
from dns._compat import xrange
class CSYNC(dns.rdata.Rdata):
"""CSYNC record
@ivar serial: the SOA serial number
@type serial: int
@ivar flags: the CSYNC flags
@type flags: int
@ivar windows: the windowed bitmap list
@type windows: list of (window number, string) tuples"""
__slots__ = ['serial', 'flags', 'windows']
def __init__(self, rdclass, rdtype, serial, flags, windows):
super(CSYNC, self).__init__(rdclass, rdtype)
self.serial = serial
self.flags = flags
self.windows = windows
def to_text(self, origin=None, relativize=True, **kw):
text = ''
for (window, bitmap) in self.windows:
bits = []
for i in xrange(0, len(bitmap)):
byte = bitmap[i]
for j in xrange(0, 8):
if byte & (0x80 >> j):
bits.append(dns.rdatatype.to_text(window * 256 +
i * 8 + j))
text += (' ' + ' '.join(bits))
return '%d %d%s' % (self.serial, self.flags, text)
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
serial = tok.get_uint32()
flags = tok.get_uint16()
rdtypes = []
while 1:
token = tok.get().unescape()
if token.is_eol_or_eof():
break
nrdtype = dns.rdatatype.from_text(token.value)
if nrdtype == 0:
raise dns.exception.SyntaxError("CSYNC with bit 0")
if nrdtype > 65535:
raise dns.exception.SyntaxError("CSYNC with bit > 65535")
rdtypes.append(nrdtype)
rdtypes.sort()
window = 0
octets = 0
prior_rdtype = 0
bitmap = bytearray(b'\0' * 32)
windows = []
for nrdtype in rdtypes:
if nrdtype == prior_rdtype:
continue
prior_rdtype = nrdtype
new_window = nrdtype // 256
if new_window != window:
windows.append((window, bitmap[0:octets]))
bitmap = bytearray(b'\0' * 32)
window = new_window
offset = nrdtype % 256
byte = offset // 8
bit = offset % 8
octets = byte + 1
bitmap[byte] = bitmap[byte] | (0x80 >> bit)
windows.append((window, bitmap[0:octets]))
return cls(rdclass, rdtype, serial, flags, windows)
def to_wire(self, file, compress=None, origin=None):
file.write(struct.pack('!IH', self.serial, self.flags))
for (window, bitmap) in self.windows:
file.write(struct.pack('!BB', window, len(bitmap)))
file.write(bitmap)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
if rdlen < 6:
raise dns.exception.FormError("CSYNC too short")
(serial, flags) = struct.unpack("!IH", wire[current: current + 6])
current += 6
rdlen -= 6
windows = []
while rdlen > 0:
if rdlen < 3:
raise dns.exception.FormError("CSYNC too short")
window = wire[current]
octets = wire[current + 1]
if octets == 0 or octets > 32:
raise dns.exception.FormError("bad CSYNC octets")
current += 2
rdlen -= 2
if rdlen < octets:
raise dns.exception.FormError("bad CSYNC bitmap length")
bitmap = bytearray(wire[current: current + octets].unwrap())
current += octets
rdlen -= octets
windows.append((window, bitmap))
return cls(rdclass, rdtype, serial, flags, windows)

View File

@@ -0,0 +1,23 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.dsbase
class DLV(dns.rdtypes.dsbase.DSBase):
"""DLV record"""

View File

@@ -0,0 +1,26 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.nsbase
class DNAME(dns.rdtypes.nsbase.UncompressedNS):
"""DNAME record"""
def to_digestable(self, origin=None):
return self.target.to_digestable(origin)

View File

@@ -0,0 +1,27 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.dnskeybase
from dns.rdtypes.dnskeybase import flags_to_text_set, flags_from_text_set
__all__ = ['flags_to_text_set', 'flags_from_text_set']
class DNSKEY(dns.rdtypes.dnskeybase.DNSKEYBase):
"""DNSKEY record"""

23
src/dns/rdtypes/ANY/DS.py Normal file
View File

@@ -0,0 +1,23 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.dsbase
class DS(dns.rdtypes.dsbase.DSBase):
"""DS record"""

View File

@@ -0,0 +1,29 @@
# Copyright (C) 2015 Red Hat, Inc.
# Author: Petr Spacek <pspacek@redhat.com>
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.euibase
class EUI48(dns.rdtypes.euibase.EUIBase):
"""EUI48 record
@ivar fingerprint: 48-bit Extended Unique Identifier (EUI-48)
@type fingerprint: string
@see: rfc7043.txt"""
byte_len = 6 # 0123456789ab (in hex)
text_len = byte_len * 3 - 1 # 01-23-45-67-89-ab

View File

@@ -0,0 +1,29 @@
# Copyright (C) 2015 Red Hat, Inc.
# Author: Petr Spacek <pspacek@redhat.com>
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.euibase
class EUI64(dns.rdtypes.euibase.EUIBase):
"""EUI64 record
@ivar fingerprint: 64-bit Extended Unique Identifier (EUI-64)
@type fingerprint: string
@see: rfc7043.txt"""
byte_len = 8 # 0123456789abcdef (in hex)
text_len = byte_len * 3 - 1 # 01-23-45-67-89-ab-cd-ef

162
src/dns/rdtypes/ANY/GPOS.py Normal file
View File

@@ -0,0 +1,162 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import dns.exception
import dns.rdata
import dns.tokenizer
from dns._compat import long, text_type
def _validate_float_string(what):
if what[0] == b'-'[0] or what[0] == b'+'[0]:
what = what[1:]
if what.isdigit():
return
(left, right) = what.split(b'.')
if left == b'' and right == b'':
raise dns.exception.FormError
if not left == b'' and not left.decode().isdigit():
raise dns.exception.FormError
if not right == b'' and not right.decode().isdigit():
raise dns.exception.FormError
def _sanitize(value):
if isinstance(value, text_type):
return value.encode()
return value
class GPOS(dns.rdata.Rdata):
"""GPOS record
@ivar latitude: latitude
@type latitude: string
@ivar longitude: longitude
@type longitude: string
@ivar altitude: altitude
@type altitude: string
@see: RFC 1712"""
__slots__ = ['latitude', 'longitude', 'altitude']
def __init__(self, rdclass, rdtype, latitude, longitude, altitude):
super(GPOS, self).__init__(rdclass, rdtype)
if isinstance(latitude, float) or \
isinstance(latitude, int) or \
isinstance(latitude, long):
latitude = str(latitude)
if isinstance(longitude, float) or \
isinstance(longitude, int) or \
isinstance(longitude, long):
longitude = str(longitude)
if isinstance(altitude, float) or \
isinstance(altitude, int) or \
isinstance(altitude, long):
altitude = str(altitude)
latitude = _sanitize(latitude)
longitude = _sanitize(longitude)
altitude = _sanitize(altitude)
_validate_float_string(latitude)
_validate_float_string(longitude)
_validate_float_string(altitude)
self.latitude = latitude
self.longitude = longitude
self.altitude = altitude
def to_text(self, origin=None, relativize=True, **kw):
return '{} {} {}'.format(self.latitude.decode(),
self.longitude.decode(),
self.altitude.decode())
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
latitude = tok.get_string()
longitude = tok.get_string()
altitude = tok.get_string()
tok.get_eol()
return cls(rdclass, rdtype, latitude, longitude, altitude)
def to_wire(self, file, compress=None, origin=None):
l = len(self.latitude)
assert l < 256
file.write(struct.pack('!B', l))
file.write(self.latitude)
l = len(self.longitude)
assert l < 256
file.write(struct.pack('!B', l))
file.write(self.longitude)
l = len(self.altitude)
assert l < 256
file.write(struct.pack('!B', l))
file.write(self.altitude)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
l = wire[current]
current += 1
rdlen -= 1
if l > rdlen:
raise dns.exception.FormError
latitude = wire[current: current + l].unwrap()
current += l
rdlen -= l
l = wire[current]
current += 1
rdlen -= 1
if l > rdlen:
raise dns.exception.FormError
longitude = wire[current: current + l].unwrap()
current += l
rdlen -= l
l = wire[current]
current += 1
rdlen -= 1
if l != rdlen:
raise dns.exception.FormError
altitude = wire[current: current + l].unwrap()
return cls(rdclass, rdtype, latitude, longitude, altitude)
def _get_float_latitude(self):
return float(self.latitude)
def _set_float_latitude(self, value):
self.latitude = str(value)
float_latitude = property(_get_float_latitude, _set_float_latitude,
doc="latitude as a floating point value")
def _get_float_longitude(self):
return float(self.longitude)
def _set_float_longitude(self, value):
self.longitude = str(value)
float_longitude = property(_get_float_longitude, _set_float_longitude,
doc="longitude as a floating point value")
def _get_float_altitude(self):
return float(self.altitude)
def _set_float_altitude(self, value):
self.altitude = str(value)
float_altitude = property(_get_float_altitude, _set_float_altitude,
doc="altitude as a floating point value")

View File

@@ -0,0 +1,86 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import dns.exception
import dns.rdata
import dns.tokenizer
from dns._compat import text_type
class HINFO(dns.rdata.Rdata):
"""HINFO record
@ivar cpu: the CPU type
@type cpu: string
@ivar os: the OS type
@type os: string
@see: RFC 1035"""
__slots__ = ['cpu', 'os']
def __init__(self, rdclass, rdtype, cpu, os):
super(HINFO, self).__init__(rdclass, rdtype)
if isinstance(cpu, text_type):
self.cpu = cpu.encode()
else:
self.cpu = cpu
if isinstance(os, text_type):
self.os = os.encode()
else:
self.os = os
def to_text(self, origin=None, relativize=True, **kw):
return '"{}" "{}"'.format(dns.rdata._escapify(self.cpu),
dns.rdata._escapify(self.os))
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
cpu = tok.get_string()
os = tok.get_string()
tok.get_eol()
return cls(rdclass, rdtype, cpu, os)
def to_wire(self, file, compress=None, origin=None):
l = len(self.cpu)
assert l < 256
file.write(struct.pack('!B', l))
file.write(self.cpu)
l = len(self.os)
assert l < 256
file.write(struct.pack('!B', l))
file.write(self.os)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
l = wire[current]
current += 1
rdlen -= 1
if l > rdlen:
raise dns.exception.FormError
cpu = wire[current:current + l].unwrap()
current += l
rdlen -= l
l = wire[current]
current += 1
rdlen -= 1
if l != rdlen:
raise dns.exception.FormError
os = wire[current: current + l].unwrap()
return cls(rdclass, rdtype, cpu, os)

115
src/dns/rdtypes/ANY/HIP.py Normal file
View File

@@ -0,0 +1,115 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2010, 2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import base64
import binascii
import dns.exception
import dns.rdata
import dns.rdatatype
class HIP(dns.rdata.Rdata):
"""HIP record
@ivar hit: the host identity tag
@type hit: string
@ivar algorithm: the public key cryptographic algorithm
@type algorithm: int
@ivar key: the public key
@type key: string
@ivar servers: the rendezvous servers
@type servers: list of dns.name.Name objects
@see: RFC 5205"""
__slots__ = ['hit', 'algorithm', 'key', 'servers']
def __init__(self, rdclass, rdtype, hit, algorithm, key, servers):
super(HIP, self).__init__(rdclass, rdtype)
self.hit = hit
self.algorithm = algorithm
self.key = key
self.servers = servers
def to_text(self, origin=None, relativize=True, **kw):
hit = binascii.hexlify(self.hit).decode()
key = base64.b64encode(self.key).replace(b'\n', b'').decode()
text = u''
servers = []
for server in self.servers:
servers.append(server.choose_relativity(origin, relativize))
if len(servers) > 0:
text += (u' ' + u' '.join((x.to_unicode() for x in servers)))
return u'%u %s %s%s' % (self.algorithm, hit, key, text)
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
algorithm = tok.get_uint8()
hit = binascii.unhexlify(tok.get_string().encode())
if len(hit) > 255:
raise dns.exception.SyntaxError("HIT too long")
key = base64.b64decode(tok.get_string().encode())
servers = []
while 1:
token = tok.get()
if token.is_eol_or_eof():
break
server = dns.name.from_text(token.value, origin)
server.choose_relativity(origin, relativize)
servers.append(server)
return cls(rdclass, rdtype, hit, algorithm, key, servers)
def to_wire(self, file, compress=None, origin=None):
lh = len(self.hit)
lk = len(self.key)
file.write(struct.pack("!BBH", lh, self.algorithm, lk))
file.write(self.hit)
file.write(self.key)
for server in self.servers:
server.to_wire(file, None, origin)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
(lh, algorithm, lk) = struct.unpack('!BBH',
wire[current: current + 4])
current += 4
rdlen -= 4
hit = wire[current: current + lh].unwrap()
current += lh
rdlen -= lh
key = wire[current: current + lk].unwrap()
current += lk
rdlen -= lk
servers = []
while rdlen > 0:
(server, cused) = dns.name.from_wire(wire[: current + rdlen],
current)
current += cused
rdlen -= cused
if origin is not None:
server = server.relativize(origin)
servers.append(server)
return cls(rdclass, rdtype, hit, algorithm, key, servers)
def choose_relativity(self, origin=None, relativize=True):
servers = []
for server in self.servers:
server = server.choose_relativity(origin, relativize)
servers.append(server)
self.servers = servers

View File

@@ -0,0 +1,99 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import dns.exception
import dns.rdata
import dns.tokenizer
from dns._compat import text_type
class ISDN(dns.rdata.Rdata):
"""ISDN record
@ivar address: the ISDN address
@type address: string
@ivar subaddress: the ISDN subaddress (or '' if not present)
@type subaddress: string
@see: RFC 1183"""
__slots__ = ['address', 'subaddress']
def __init__(self, rdclass, rdtype, address, subaddress):
super(ISDN, self).__init__(rdclass, rdtype)
if isinstance(address, text_type):
self.address = address.encode()
else:
self.address = address
if isinstance(address, text_type):
self.subaddress = subaddress.encode()
else:
self.subaddress = subaddress
def to_text(self, origin=None, relativize=True, **kw):
if self.subaddress:
return '"{}" "{}"'.format(dns.rdata._escapify(self.address),
dns.rdata._escapify(self.subaddress))
else:
return '"%s"' % dns.rdata._escapify(self.address)
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
address = tok.get_string()
t = tok.get()
if not t.is_eol_or_eof():
tok.unget(t)
subaddress = tok.get_string()
else:
tok.unget(t)
subaddress = ''
tok.get_eol()
return cls(rdclass, rdtype, address, subaddress)
def to_wire(self, file, compress=None, origin=None):
l = len(self.address)
assert l < 256
file.write(struct.pack('!B', l))
file.write(self.address)
l = len(self.subaddress)
if l > 0:
assert l < 256
file.write(struct.pack('!B', l))
file.write(self.subaddress)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
l = wire[current]
current += 1
rdlen -= 1
if l > rdlen:
raise dns.exception.FormError
address = wire[current: current + l].unwrap()
current += l
rdlen -= l
if rdlen > 0:
l = wire[current]
current += 1
rdlen -= 1
if l != rdlen:
raise dns.exception.FormError
subaddress = wire[current: current + l].unwrap()
else:
subaddress = ''
return cls(rdclass, rdtype, address, subaddress)

327
src/dns/rdtypes/ANY/LOC.py Normal file
View File

@@ -0,0 +1,327 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
from __future__ import division
import struct
import dns.exception
import dns.rdata
from dns._compat import long, xrange, round_py2_compat
_pows = tuple(long(10**i) for i in range(0, 11))
# default values are in centimeters
_default_size = 100.0
_default_hprec = 1000000.0
_default_vprec = 1000.0
def _exponent_of(what, desc):
if what == 0:
return 0
exp = None
for i in xrange(len(_pows)):
if what // _pows[i] == long(0):
exp = i - 1
break
if exp is None or exp < 0:
raise dns.exception.SyntaxError("%s value out of bounds" % desc)
return exp
def _float_to_tuple(what):
if what < 0:
sign = -1
what *= -1
else:
sign = 1
what = round_py2_compat(what * 3600000)
degrees = int(what // 3600000)
what -= degrees * 3600000
minutes = int(what // 60000)
what -= minutes * 60000
seconds = int(what // 1000)
what -= int(seconds * 1000)
what = int(what)
return (degrees, minutes, seconds, what, sign)
def _tuple_to_float(what):
value = float(what[0])
value += float(what[1]) / 60.0
value += float(what[2]) / 3600.0
value += float(what[3]) / 3600000.0
return float(what[4]) * value
def _encode_size(what, desc):
what = long(what)
exponent = _exponent_of(what, desc) & 0xF
base = what // pow(10, exponent) & 0xF
return base * 16 + exponent
def _decode_size(what, desc):
exponent = what & 0x0F
if exponent > 9:
raise dns.exception.SyntaxError("bad %s exponent" % desc)
base = (what & 0xF0) >> 4
if base > 9:
raise dns.exception.SyntaxError("bad %s base" % desc)
return long(base) * pow(10, exponent)
class LOC(dns.rdata.Rdata):
"""LOC record
@ivar latitude: latitude
@type latitude: (int, int, int, int, sign) tuple specifying the degrees, minutes,
seconds, milliseconds, and sign of the coordinate.
@ivar longitude: longitude
@type longitude: (int, int, int, int, sign) tuple specifying the degrees,
minutes, seconds, milliseconds, and sign of the coordinate.
@ivar altitude: altitude
@type altitude: float
@ivar size: size of the sphere
@type size: float
@ivar horizontal_precision: horizontal precision
@type horizontal_precision: float
@ivar vertical_precision: vertical precision
@type vertical_precision: float
@see: RFC 1876"""
__slots__ = ['latitude', 'longitude', 'altitude', 'size',
'horizontal_precision', 'vertical_precision']
def __init__(self, rdclass, rdtype, latitude, longitude, altitude,
size=_default_size, hprec=_default_hprec,
vprec=_default_vprec):
"""Initialize a LOC record instance.
The parameters I{latitude} and I{longitude} may be either a 4-tuple
of integers specifying (degrees, minutes, seconds, milliseconds),
or they may be floating point values specifying the number of
degrees. The other parameters are floats. Size, horizontal precision,
and vertical precision are specified in centimeters."""
super(LOC, self).__init__(rdclass, rdtype)
if isinstance(latitude, int) or isinstance(latitude, long):
latitude = float(latitude)
if isinstance(latitude, float):
latitude = _float_to_tuple(latitude)
self.latitude = latitude
if isinstance(longitude, int) or isinstance(longitude, long):
longitude = float(longitude)
if isinstance(longitude, float):
longitude = _float_to_tuple(longitude)
self.longitude = longitude
self.altitude = float(altitude)
self.size = float(size)
self.horizontal_precision = float(hprec)
self.vertical_precision = float(vprec)
def to_text(self, origin=None, relativize=True, **kw):
if self.latitude[4] > 0:
lat_hemisphere = 'N'
else:
lat_hemisphere = 'S'
if self.longitude[4] > 0:
long_hemisphere = 'E'
else:
long_hemisphere = 'W'
text = "%d %d %d.%03d %s %d %d %d.%03d %s %0.2fm" % (
self.latitude[0], self.latitude[1],
self.latitude[2], self.latitude[3], lat_hemisphere,
self.longitude[0], self.longitude[1], self.longitude[2],
self.longitude[3], long_hemisphere,
self.altitude / 100.0
)
# do not print default values
if self.size != _default_size or \
self.horizontal_precision != _default_hprec or \
self.vertical_precision != _default_vprec:
text += " {:0.2f}m {:0.2f}m {:0.2f}m".format(
self.size / 100.0, self.horizontal_precision / 100.0,
self.vertical_precision / 100.0
)
return text
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
latitude = [0, 0, 0, 0, 1]
longitude = [0, 0, 0, 0, 1]
size = _default_size
hprec = _default_hprec
vprec = _default_vprec
latitude[0] = tok.get_int()
t = tok.get_string()
if t.isdigit():
latitude[1] = int(t)
t = tok.get_string()
if '.' in t:
(seconds, milliseconds) = t.split('.')
if not seconds.isdigit():
raise dns.exception.SyntaxError(
'bad latitude seconds value')
latitude[2] = int(seconds)
if latitude[2] >= 60:
raise dns.exception.SyntaxError('latitude seconds >= 60')
l = len(milliseconds)
if l == 0 or l > 3 or not milliseconds.isdigit():
raise dns.exception.SyntaxError(
'bad latitude milliseconds value')
if l == 1:
m = 100
elif l == 2:
m = 10
else:
m = 1
latitude[3] = m * int(milliseconds)
t = tok.get_string()
elif t.isdigit():
latitude[2] = int(t)
t = tok.get_string()
if t == 'S':
latitude[4] = -1
elif t != 'N':
raise dns.exception.SyntaxError('bad latitude hemisphere value')
longitude[0] = tok.get_int()
t = tok.get_string()
if t.isdigit():
longitude[1] = int(t)
t = tok.get_string()
if '.' in t:
(seconds, milliseconds) = t.split('.')
if not seconds.isdigit():
raise dns.exception.SyntaxError(
'bad longitude seconds value')
longitude[2] = int(seconds)
if longitude[2] >= 60:
raise dns.exception.SyntaxError('longitude seconds >= 60')
l = len(milliseconds)
if l == 0 or l > 3 or not milliseconds.isdigit():
raise dns.exception.SyntaxError(
'bad longitude milliseconds value')
if l == 1:
m = 100
elif l == 2:
m = 10
else:
m = 1
longitude[3] = m * int(milliseconds)
t = tok.get_string()
elif t.isdigit():
longitude[2] = int(t)
t = tok.get_string()
if t == 'W':
longitude[4] = -1
elif t != 'E':
raise dns.exception.SyntaxError('bad longitude hemisphere value')
t = tok.get_string()
if t[-1] == 'm':
t = t[0: -1]
altitude = float(t) * 100.0 # m -> cm
token = tok.get().unescape()
if not token.is_eol_or_eof():
value = token.value
if value[-1] == 'm':
value = value[0: -1]
size = float(value) * 100.0 # m -> cm
token = tok.get().unescape()
if not token.is_eol_or_eof():
value = token.value
if value[-1] == 'm':
value = value[0: -1]
hprec = float(value) * 100.0 # m -> cm
token = tok.get().unescape()
if not token.is_eol_or_eof():
value = token.value
if value[-1] == 'm':
value = value[0: -1]
vprec = float(value) * 100.0 # m -> cm
tok.get_eol()
return cls(rdclass, rdtype, latitude, longitude, altitude,
size, hprec, vprec)
def to_wire(self, file, compress=None, origin=None):
milliseconds = (self.latitude[0] * 3600000 +
self.latitude[1] * 60000 +
self.latitude[2] * 1000 +
self.latitude[3]) * self.latitude[4]
latitude = long(0x80000000) + milliseconds
milliseconds = (self.longitude[0] * 3600000 +
self.longitude[1] * 60000 +
self.longitude[2] * 1000 +
self.longitude[3]) * self.longitude[4]
longitude = long(0x80000000) + milliseconds
altitude = long(self.altitude) + long(10000000)
size = _encode_size(self.size, "size")
hprec = _encode_size(self.horizontal_precision, "horizontal precision")
vprec = _encode_size(self.vertical_precision, "vertical precision")
wire = struct.pack("!BBBBIII", 0, size, hprec, vprec, latitude,
longitude, altitude)
file.write(wire)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
(version, size, hprec, vprec, latitude, longitude, altitude) = \
struct.unpack("!BBBBIII", wire[current: current + rdlen])
if latitude > long(0x80000000):
latitude = float(latitude - long(0x80000000)) / 3600000
else:
latitude = -1 * float(long(0x80000000) - latitude) / 3600000
if latitude < -90.0 or latitude > 90.0:
raise dns.exception.FormError("bad latitude")
if longitude > long(0x80000000):
longitude = float(longitude - long(0x80000000)) / 3600000
else:
longitude = -1 * float(long(0x80000000) - longitude) / 3600000
if longitude < -180.0 or longitude > 180.0:
raise dns.exception.FormError("bad longitude")
altitude = float(altitude) - 10000000.0
size = _decode_size(size, "size")
hprec = _decode_size(hprec, "horizontal precision")
vprec = _decode_size(vprec, "vertical precision")
return cls(rdclass, rdtype, latitude, longitude, altitude,
size, hprec, vprec)
def _get_float_latitude(self):
return _tuple_to_float(self.latitude)
def _set_float_latitude(self, value):
self.latitude = _float_to_tuple(value)
float_latitude = property(_get_float_latitude, _set_float_latitude,
doc="latitude as a floating point value")
def _get_float_longitude(self):
return _tuple_to_float(self.longitude)
def _set_float_longitude(self, value):
self.longitude = _float_to_tuple(value)
float_longitude = property(_get_float_longitude, _set_float_longitude,
doc="longitude as a floating point value")

23
src/dns/rdtypes/ANY/MX.py Normal file
View File

@@ -0,0 +1,23 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.mxbase
class MX(dns.rdtypes.mxbase.MXBase):
"""MX record"""

23
src/dns/rdtypes/ANY/NS.py Normal file
View File

@@ -0,0 +1,23 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.nsbase
class NS(dns.rdtypes.nsbase.NSBase):
"""NS record"""

128
src/dns/rdtypes/ANY/NSEC.py Normal file
View File

@@ -0,0 +1,128 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import dns.exception
import dns.rdata
import dns.rdatatype
import dns.name
from dns._compat import xrange
class NSEC(dns.rdata.Rdata):
"""NSEC record
@ivar next: the next name
@type next: dns.name.Name object
@ivar windows: the windowed bitmap list
@type windows: list of (window number, string) tuples"""
__slots__ = ['next', 'windows']
def __init__(self, rdclass, rdtype, next, windows):
super(NSEC, self).__init__(rdclass, rdtype)
self.next = next
self.windows = windows
def to_text(self, origin=None, relativize=True, **kw):
next = self.next.choose_relativity(origin, relativize)
text = ''
for (window, bitmap) in self.windows:
bits = []
for i in xrange(0, len(bitmap)):
byte = bitmap[i]
for j in xrange(0, 8):
if byte & (0x80 >> j):
bits.append(dns.rdatatype.to_text(window * 256 +
i * 8 + j))
text += (' ' + ' '.join(bits))
return '{}{}'.format(next, text)
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
next = tok.get_name()
next = next.choose_relativity(origin, relativize)
rdtypes = []
while 1:
token = tok.get().unescape()
if token.is_eol_or_eof():
break
nrdtype = dns.rdatatype.from_text(token.value)
if nrdtype == 0:
raise dns.exception.SyntaxError("NSEC with bit 0")
if nrdtype > 65535:
raise dns.exception.SyntaxError("NSEC with bit > 65535")
rdtypes.append(nrdtype)
rdtypes.sort()
window = 0
octets = 0
prior_rdtype = 0
bitmap = bytearray(b'\0' * 32)
windows = []
for nrdtype in rdtypes:
if nrdtype == prior_rdtype:
continue
prior_rdtype = nrdtype
new_window = nrdtype // 256
if new_window != window:
windows.append((window, bitmap[0:octets]))
bitmap = bytearray(b'\0' * 32)
window = new_window
offset = nrdtype % 256
byte = offset // 8
bit = offset % 8
octets = byte + 1
bitmap[byte] = bitmap[byte] | (0x80 >> bit)
windows.append((window, bitmap[0:octets]))
return cls(rdclass, rdtype, next, windows)
def to_wire(self, file, compress=None, origin=None):
self.next.to_wire(file, None, origin)
for (window, bitmap) in self.windows:
file.write(struct.pack('!BB', window, len(bitmap)))
file.write(bitmap)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
(next, cused) = dns.name.from_wire(wire[: current + rdlen], current)
current += cused
rdlen -= cused
windows = []
while rdlen > 0:
if rdlen < 3:
raise dns.exception.FormError("NSEC too short")
window = wire[current]
octets = wire[current + 1]
if octets == 0 or octets > 32:
raise dns.exception.FormError("bad NSEC octets")
current += 2
rdlen -= 2
if rdlen < octets:
raise dns.exception.FormError("bad NSEC bitmap length")
bitmap = bytearray(wire[current: current + octets].unwrap())
current += octets
rdlen -= octets
windows.append((window, bitmap))
if origin is not None:
next = next.relativize(origin)
return cls(rdclass, rdtype, next, windows)
def choose_relativity(self, origin=None, relativize=True):
self.next = self.next.choose_relativity(origin, relativize)

View File

@@ -0,0 +1,196 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2004-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import base64
import binascii
import string
import struct
import dns.exception
import dns.rdata
import dns.rdatatype
from dns._compat import xrange, text_type, PY3
# pylint: disable=deprecated-string-function
if PY3:
b32_hex_to_normal = bytes.maketrans(b'0123456789ABCDEFGHIJKLMNOPQRSTUV',
b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
b32_normal_to_hex = bytes.maketrans(b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',
b'0123456789ABCDEFGHIJKLMNOPQRSTUV')
else:
b32_hex_to_normal = string.maketrans('0123456789ABCDEFGHIJKLMNOPQRSTUV',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
b32_normal_to_hex = string.maketrans('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',
'0123456789ABCDEFGHIJKLMNOPQRSTUV')
# pylint: enable=deprecated-string-function
# hash algorithm constants
SHA1 = 1
# flag constants
OPTOUT = 1
class NSEC3(dns.rdata.Rdata):
"""NSEC3 record
@ivar algorithm: the hash algorithm number
@type algorithm: int
@ivar flags: the flags
@type flags: int
@ivar iterations: the number of iterations
@type iterations: int
@ivar salt: the salt
@type salt: string
@ivar next: the next name hash
@type next: string
@ivar windows: the windowed bitmap list
@type windows: list of (window number, string) tuples"""
__slots__ = ['algorithm', 'flags', 'iterations', 'salt', 'next', 'windows']
def __init__(self, rdclass, rdtype, algorithm, flags, iterations, salt,
next, windows):
super(NSEC3, self).__init__(rdclass, rdtype)
self.algorithm = algorithm
self.flags = flags
self.iterations = iterations
if isinstance(salt, text_type):
self.salt = salt.encode()
else:
self.salt = salt
self.next = next
self.windows = windows
def to_text(self, origin=None, relativize=True, **kw):
next = base64.b32encode(self.next).translate(
b32_normal_to_hex).lower().decode()
if self.salt == b'':
salt = '-'
else:
salt = binascii.hexlify(self.salt).decode()
text = u''
for (window, bitmap) in self.windows:
bits = []
for i in xrange(0, len(bitmap)):
byte = bitmap[i]
for j in xrange(0, 8):
if byte & (0x80 >> j):
bits.append(dns.rdatatype.to_text(window * 256 +
i * 8 + j))
text += (u' ' + u' '.join(bits))
return u'%u %u %u %s %s%s' % (self.algorithm, self.flags,
self.iterations, salt, next, text)
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
algorithm = tok.get_uint8()
flags = tok.get_uint8()
iterations = tok.get_uint16()
salt = tok.get_string()
if salt == u'-':
salt = b''
else:
salt = binascii.unhexlify(salt.encode('ascii'))
next = tok.get_string().encode(
'ascii').upper().translate(b32_hex_to_normal)
next = base64.b32decode(next)
rdtypes = []
while 1:
token = tok.get().unescape()
if token.is_eol_or_eof():
break
nrdtype = dns.rdatatype.from_text(token.value)
if nrdtype == 0:
raise dns.exception.SyntaxError("NSEC3 with bit 0")
if nrdtype > 65535:
raise dns.exception.SyntaxError("NSEC3 with bit > 65535")
rdtypes.append(nrdtype)
rdtypes.sort()
window = 0
octets = 0
prior_rdtype = 0
bitmap = bytearray(b'\0' * 32)
windows = []
for nrdtype in rdtypes:
if nrdtype == prior_rdtype:
continue
prior_rdtype = nrdtype
new_window = nrdtype // 256
if new_window != window:
if octets != 0:
windows.append((window, bitmap[0:octets]))
bitmap = bytearray(b'\0' * 32)
window = new_window
offset = nrdtype % 256
byte = offset // 8
bit = offset % 8
octets = byte + 1
bitmap[byte] = bitmap[byte] | (0x80 >> bit)
if octets != 0:
windows.append((window, bitmap[0:octets]))
return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next,
windows)
def to_wire(self, file, compress=None, origin=None):
l = len(self.salt)
file.write(struct.pack("!BBHB", self.algorithm, self.flags,
self.iterations, l))
file.write(self.salt)
l = len(self.next)
file.write(struct.pack("!B", l))
file.write(self.next)
for (window, bitmap) in self.windows:
file.write(struct.pack("!BB", window, len(bitmap)))
file.write(bitmap)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
(algorithm, flags, iterations, slen) = \
struct.unpack('!BBHB', wire[current: current + 5])
current += 5
rdlen -= 5
salt = wire[current: current + slen].unwrap()
current += slen
rdlen -= slen
nlen = wire[current]
current += 1
rdlen -= 1
next = wire[current: current + nlen].unwrap()
current += nlen
rdlen -= nlen
windows = []
while rdlen > 0:
if rdlen < 3:
raise dns.exception.FormError("NSEC3 too short")
window = wire[current]
octets = wire[current + 1]
if octets == 0 or octets > 32:
raise dns.exception.FormError("bad NSEC3 octets")
current += 2
rdlen -= 2
if rdlen < octets:
raise dns.exception.FormError("bad NSEC3 bitmap length")
bitmap = bytearray(wire[current: current + octets].unwrap())
current += octets
rdlen -= octets
windows.append((window, bitmap))
return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next,
windows)

View File

@@ -0,0 +1,90 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import binascii
import dns.exception
import dns.rdata
from dns._compat import text_type
class NSEC3PARAM(dns.rdata.Rdata):
"""NSEC3PARAM record
@ivar algorithm: the hash algorithm number
@type algorithm: int
@ivar flags: the flags
@type flags: int
@ivar iterations: the number of iterations
@type iterations: int
@ivar salt: the salt
@type salt: string"""
__slots__ = ['algorithm', 'flags', 'iterations', 'salt']
def __init__(self, rdclass, rdtype, algorithm, flags, iterations, salt):
super(NSEC3PARAM, self).__init__(rdclass, rdtype)
self.algorithm = algorithm
self.flags = flags
self.iterations = iterations
if isinstance(salt, text_type):
self.salt = salt.encode()
else:
self.salt = salt
def to_text(self, origin=None, relativize=True, **kw):
if self.salt == b'':
salt = '-'
else:
salt = binascii.hexlify(self.salt).decode()
return '%u %u %u %s' % (self.algorithm, self.flags, self.iterations,
salt)
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
algorithm = tok.get_uint8()
flags = tok.get_uint8()
iterations = tok.get_uint16()
salt = tok.get_string()
if salt == '-':
salt = ''
else:
salt = binascii.unhexlify(salt.encode())
tok.get_eol()
return cls(rdclass, rdtype, algorithm, flags, iterations, salt)
def to_wire(self, file, compress=None, origin=None):
l = len(self.salt)
file.write(struct.pack("!BBHB", self.algorithm, self.flags,
self.iterations, l))
file.write(self.salt)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
(algorithm, flags, iterations, slen) = \
struct.unpack('!BBHB',
wire[current: current + 5])
current += 5
rdlen -= 5
salt = wire[current: current + slen].unwrap()
current += slen
rdlen -= slen
if rdlen != 0:
raise dns.exception.FormError
return cls(rdclass, rdtype, algorithm, flags, iterations, salt)

View File

@@ -0,0 +1,60 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2016 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import base64
import dns.exception
import dns.rdata
import dns.tokenizer
class OPENPGPKEY(dns.rdata.Rdata):
"""OPENPGPKEY record
@ivar key: the key
@type key: bytes
@see: RFC 7929
"""
def __init__(self, rdclass, rdtype, key):
super(OPENPGPKEY, self).__init__(rdclass, rdtype)
self.key = key
def to_text(self, origin=None, relativize=True, **kw):
return dns.rdata._base64ify(self.key)
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
chunks = []
while 1:
t = tok.get().unescape()
if t.is_eol_or_eof():
break
if not t.is_identifier():
raise dns.exception.SyntaxError
chunks.append(t.value.encode())
b64 = b''.join(chunks)
key = base64.b64decode(b64)
return cls(rdclass, rdtype, key)
def to_wire(self, file, compress=None, origin=None):
file.write(self.key)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
key = wire[current: current + rdlen].unwrap()
return cls(rdclass, rdtype, key)

View File

@@ -0,0 +1,23 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.nsbase
class PTR(dns.rdtypes.nsbase.NSBase):
"""PTR record"""

82
src/dns/rdtypes/ANY/RP.py Normal file
View File

@@ -0,0 +1,82 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.exception
import dns.rdata
import dns.name
class RP(dns.rdata.Rdata):
"""RP record
@ivar mbox: The responsible person's mailbox
@type mbox: dns.name.Name object
@ivar txt: The owner name of a node with TXT records, or the root name
if no TXT records are associated with this RP.
@type txt: dns.name.Name object
@see: RFC 1183"""
__slots__ = ['mbox', 'txt']
def __init__(self, rdclass, rdtype, mbox, txt):
super(RP, self).__init__(rdclass, rdtype)
self.mbox = mbox
self.txt = txt
def to_text(self, origin=None, relativize=True, **kw):
mbox = self.mbox.choose_relativity(origin, relativize)
txt = self.txt.choose_relativity(origin, relativize)
return "{} {}".format(str(mbox), str(txt))
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
mbox = tok.get_name()
txt = tok.get_name()
mbox = mbox.choose_relativity(origin, relativize)
txt = txt.choose_relativity(origin, relativize)
tok.get_eol()
return cls(rdclass, rdtype, mbox, txt)
def to_wire(self, file, compress=None, origin=None):
self.mbox.to_wire(file, None, origin)
self.txt.to_wire(file, None, origin)
def to_digestable(self, origin=None):
return self.mbox.to_digestable(origin) + \
self.txt.to_digestable(origin)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
(mbox, cused) = dns.name.from_wire(wire[: current + rdlen],
current)
current += cused
rdlen -= cused
if rdlen <= 0:
raise dns.exception.FormError
(txt, cused) = dns.name.from_wire(wire[: current + rdlen],
current)
if cused != rdlen:
raise dns.exception.FormError
if origin is not None:
mbox = mbox.relativize(origin)
txt = txt.relativize(origin)
return cls(rdclass, rdtype, mbox, txt)
def choose_relativity(self, origin=None, relativize=True):
self.mbox = self.mbox.choose_relativity(origin, relativize)
self.txt = self.txt.choose_relativity(origin, relativize)

View File

@@ -0,0 +1,158 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import base64
import calendar
import struct
import time
import dns.dnssec
import dns.exception
import dns.rdata
import dns.rdatatype
class BadSigTime(dns.exception.DNSException):
"""Time in DNS SIG or RRSIG resource record cannot be parsed."""
def sigtime_to_posixtime(what):
if len(what) != 14:
raise BadSigTime
year = int(what[0:4])
month = int(what[4:6])
day = int(what[6:8])
hour = int(what[8:10])
minute = int(what[10:12])
second = int(what[12:14])
return calendar.timegm((year, month, day, hour, minute, second,
0, 0, 0))
def posixtime_to_sigtime(what):
return time.strftime('%Y%m%d%H%M%S', time.gmtime(what))
class RRSIG(dns.rdata.Rdata):
"""RRSIG record
@ivar type_covered: the rdata type this signature covers
@type type_covered: int
@ivar algorithm: the algorithm used for the sig
@type algorithm: int
@ivar labels: number of labels
@type labels: int
@ivar original_ttl: the original TTL
@type original_ttl: long
@ivar expiration: signature expiration time
@type expiration: long
@ivar inception: signature inception time
@type inception: long
@ivar key_tag: the key tag
@type key_tag: int
@ivar signer: the signer
@type signer: dns.name.Name object
@ivar signature: the signature
@type signature: string"""
__slots__ = ['type_covered', 'algorithm', 'labels', 'original_ttl',
'expiration', 'inception', 'key_tag', 'signer',
'signature']
def __init__(self, rdclass, rdtype, type_covered, algorithm, labels,
original_ttl, expiration, inception, key_tag, signer,
signature):
super(RRSIG, self).__init__(rdclass, rdtype)
self.type_covered = type_covered
self.algorithm = algorithm
self.labels = labels
self.original_ttl = original_ttl
self.expiration = expiration
self.inception = inception
self.key_tag = key_tag
self.signer = signer
self.signature = signature
def covers(self):
return self.type_covered
def to_text(self, origin=None, relativize=True, **kw):
return '%s %d %d %d %s %s %d %s %s' % (
dns.rdatatype.to_text(self.type_covered),
self.algorithm,
self.labels,
self.original_ttl,
posixtime_to_sigtime(self.expiration),
posixtime_to_sigtime(self.inception),
self.key_tag,
self.signer.choose_relativity(origin, relativize),
dns.rdata._base64ify(self.signature)
)
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
type_covered = dns.rdatatype.from_text(tok.get_string())
algorithm = dns.dnssec.algorithm_from_text(tok.get_string())
labels = tok.get_int()
original_ttl = tok.get_ttl()
expiration = sigtime_to_posixtime(tok.get_string())
inception = sigtime_to_posixtime(tok.get_string())
key_tag = tok.get_int()
signer = tok.get_name()
signer = signer.choose_relativity(origin, relativize)
chunks = []
while 1:
t = tok.get().unescape()
if t.is_eol_or_eof():
break
if not t.is_identifier():
raise dns.exception.SyntaxError
chunks.append(t.value.encode())
b64 = b''.join(chunks)
signature = base64.b64decode(b64)
return cls(rdclass, rdtype, type_covered, algorithm, labels,
original_ttl, expiration, inception, key_tag, signer,
signature)
def to_wire(self, file, compress=None, origin=None):
header = struct.pack('!HBBIIIH', self.type_covered,
self.algorithm, self.labels,
self.original_ttl, self.expiration,
self.inception, self.key_tag)
file.write(header)
self.signer.to_wire(file, None, origin)
file.write(self.signature)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
header = struct.unpack('!HBBIIIH', wire[current: current + 18])
current += 18
rdlen -= 18
(signer, cused) = dns.name.from_wire(wire[: current + rdlen], current)
current += cused
rdlen -= cused
if origin is not None:
signer = signer.relativize(origin)
signature = wire[current: current + rdlen].unwrap()
return cls(rdclass, rdtype, header[0], header[1], header[2],
header[3], header[4], header[5], header[6], signer,
signature)
def choose_relativity(self, origin=None, relativize=True):
self.signer = self.signer.choose_relativity(origin, relativize)

23
src/dns/rdtypes/ANY/RT.py Normal file
View File

@@ -0,0 +1,23 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.mxbase
class RT(dns.rdtypes.mxbase.UncompressedDowncasingMX):
"""RT record"""

116
src/dns/rdtypes/ANY/SOA.py Normal file
View File

@@ -0,0 +1,116 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import dns.exception
import dns.rdata
import dns.name
class SOA(dns.rdata.Rdata):
"""SOA record
@ivar mname: the SOA MNAME (master name) field
@type mname: dns.name.Name object
@ivar rname: the SOA RNAME (responsible name) field
@type rname: dns.name.Name object
@ivar serial: The zone's serial number
@type serial: int
@ivar refresh: The zone's refresh value (in seconds)
@type refresh: int
@ivar retry: The zone's retry value (in seconds)
@type retry: int
@ivar expire: The zone's expiration value (in seconds)
@type expire: int
@ivar minimum: The zone's negative caching time (in seconds, called
"minimum" for historical reasons)
@type minimum: int
@see: RFC 1035"""
__slots__ = ['mname', 'rname', 'serial', 'refresh', 'retry', 'expire',
'minimum']
def __init__(self, rdclass, rdtype, mname, rname, serial, refresh, retry,
expire, minimum):
super(SOA, self).__init__(rdclass, rdtype)
self.mname = mname
self.rname = rname
self.serial = serial
self.refresh = refresh
self.retry = retry
self.expire = expire
self.minimum = minimum
def to_text(self, origin=None, relativize=True, **kw):
mname = self.mname.choose_relativity(origin, relativize)
rname = self.rname.choose_relativity(origin, relativize)
return '%s %s %d %d %d %d %d' % (
mname, rname, self.serial, self.refresh, self.retry,
self.expire, self.minimum)
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
mname = tok.get_name()
rname = tok.get_name()
mname = mname.choose_relativity(origin, relativize)
rname = rname.choose_relativity(origin, relativize)
serial = tok.get_uint32()
refresh = tok.get_ttl()
retry = tok.get_ttl()
expire = tok.get_ttl()
minimum = tok.get_ttl()
tok.get_eol()
return cls(rdclass, rdtype, mname, rname, serial, refresh, retry,
expire, minimum)
def to_wire(self, file, compress=None, origin=None):
self.mname.to_wire(file, compress, origin)
self.rname.to_wire(file, compress, origin)
five_ints = struct.pack('!IIIII', self.serial, self.refresh,
self.retry, self.expire, self.minimum)
file.write(five_ints)
def to_digestable(self, origin=None):
return self.mname.to_digestable(origin) + \
self.rname.to_digestable(origin) + \
struct.pack('!IIIII', self.serial, self.refresh,
self.retry, self.expire, self.minimum)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
(mname, cused) = dns.name.from_wire(wire[: current + rdlen], current)
current += cused
rdlen -= cused
(rname, cused) = dns.name.from_wire(wire[: current + rdlen], current)
current += cused
rdlen -= cused
if rdlen != 20:
raise dns.exception.FormError
five_ints = struct.unpack('!IIIII',
wire[current: current + rdlen])
if origin is not None:
mname = mname.relativize(origin)
rname = rname.relativize(origin)
return cls(rdclass, rdtype, mname, rname,
five_ints[0], five_ints[1], five_ints[2], five_ints[3],
five_ints[4])
def choose_relativity(self, origin=None, relativize=True):
self.mname = self.mname.choose_relativity(origin, relativize)
self.rname = self.rname.choose_relativity(origin, relativize)

View File

@@ -0,0 +1,25 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.txtbase
class SPF(dns.rdtypes.txtbase.TXTBase):
"""SPF record
@see: RFC 4408"""

View File

@@ -0,0 +1,79 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2005-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import binascii
import dns.rdata
import dns.rdatatype
class SSHFP(dns.rdata.Rdata):
"""SSHFP record
@ivar algorithm: the algorithm
@type algorithm: int
@ivar fp_type: the digest type
@type fp_type: int
@ivar fingerprint: the fingerprint
@type fingerprint: string
@see: draft-ietf-secsh-dns-05.txt"""
__slots__ = ['algorithm', 'fp_type', 'fingerprint']
def __init__(self, rdclass, rdtype, algorithm, fp_type,
fingerprint):
super(SSHFP, self).__init__(rdclass, rdtype)
self.algorithm = algorithm
self.fp_type = fp_type
self.fingerprint = fingerprint
def to_text(self, origin=None, relativize=True, **kw):
return '%d %d %s' % (self.algorithm,
self.fp_type,
dns.rdata._hexify(self.fingerprint,
chunksize=128))
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
algorithm = tok.get_uint8()
fp_type = tok.get_uint8()
chunks = []
while 1:
t = tok.get().unescape()
if t.is_eol_or_eof():
break
if not t.is_identifier():
raise dns.exception.SyntaxError
chunks.append(t.value.encode())
fingerprint = b''.join(chunks)
fingerprint = binascii.unhexlify(fingerprint)
return cls(rdclass, rdtype, algorithm, fp_type, fingerprint)
def to_wire(self, file, compress=None, origin=None):
header = struct.pack("!BB", self.algorithm, self.fp_type)
file.write(header)
file.write(self.fingerprint)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
header = struct.unpack("!BB", wire[current: current + 2])
current += 2
rdlen -= 2
fingerprint = wire[current: current + rdlen].unwrap()
return cls(rdclass, rdtype, header[0], header[1], fingerprint)

View File

@@ -0,0 +1,84 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2005-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import binascii
import dns.rdata
import dns.rdatatype
class TLSA(dns.rdata.Rdata):
"""TLSA record
@ivar usage: The certificate usage
@type usage: int
@ivar selector: The selector field
@type selector: int
@ivar mtype: The 'matching type' field
@type mtype: int
@ivar cert: The 'Certificate Association Data' field
@type cert: string
@see: RFC 6698"""
__slots__ = ['usage', 'selector', 'mtype', 'cert']
def __init__(self, rdclass, rdtype, usage, selector,
mtype, cert):
super(TLSA, self).__init__(rdclass, rdtype)
self.usage = usage
self.selector = selector
self.mtype = mtype
self.cert = cert
def to_text(self, origin=None, relativize=True, **kw):
return '%d %d %d %s' % (self.usage,
self.selector,
self.mtype,
dns.rdata._hexify(self.cert,
chunksize=128))
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
usage = tok.get_uint8()
selector = tok.get_uint8()
mtype = tok.get_uint8()
cert_chunks = []
while 1:
t = tok.get().unescape()
if t.is_eol_or_eof():
break
if not t.is_identifier():
raise dns.exception.SyntaxError
cert_chunks.append(t.value.encode())
cert = b''.join(cert_chunks)
cert = binascii.unhexlify(cert)
return cls(rdclass, rdtype, usage, selector, mtype, cert)
def to_wire(self, file, compress=None, origin=None):
header = struct.pack("!BBB", self.usage, self.selector, self.mtype)
file.write(header)
file.write(self.cert)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
header = struct.unpack("!BBB", wire[current: current + 3])
current += 3
rdlen -= 3
cert = wire[current: current + rdlen].unwrap()
return cls(rdclass, rdtype, header[0], header[1], header[2], cert)

View File

@@ -0,0 +1,23 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.txtbase
class TXT(dns.rdtypes.txtbase.TXTBase):
"""TXT record"""

View File

@@ -0,0 +1,82 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
# Copyright (C) 2015 Red Hat, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import dns.exception
import dns.rdata
import dns.name
from dns._compat import text_type
class URI(dns.rdata.Rdata):
"""URI record
@ivar priority: the priority
@type priority: int
@ivar weight: the weight
@type weight: int
@ivar target: the target host
@type target: dns.name.Name object
@see: draft-faltstrom-uri-13"""
__slots__ = ['priority', 'weight', 'target']
def __init__(self, rdclass, rdtype, priority, weight, target):
super(URI, self).__init__(rdclass, rdtype)
self.priority = priority
self.weight = weight
if len(target) < 1:
raise dns.exception.SyntaxError("URI target cannot be empty")
if isinstance(target, text_type):
self.target = target.encode()
else:
self.target = target
def to_text(self, origin=None, relativize=True, **kw):
return '%d %d "%s"' % (self.priority, self.weight,
self.target.decode())
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
priority = tok.get_uint16()
weight = tok.get_uint16()
target = tok.get().unescape()
if not (target.is_quoted_string() or target.is_identifier()):
raise dns.exception.SyntaxError("URI target must be a string")
tok.get_eol()
return cls(rdclass, rdtype, priority, weight, target.value)
def to_wire(self, file, compress=None, origin=None):
two_ints = struct.pack("!HH", self.priority, self.weight)
file.write(two_ints)
file.write(self.target)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
if rdlen < 5:
raise dns.exception.FormError('URI RR is shorter than 5 octets')
(priority, weight) = struct.unpack('!HH', wire[current: current + 4])
current += 4
rdlen -= 4
target = wire[current: current + rdlen]
current += rdlen
return cls(rdclass, rdtype, priority, weight, target)

View File

@@ -0,0 +1,66 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import dns.exception
import dns.rdata
import dns.tokenizer
from dns._compat import text_type
class X25(dns.rdata.Rdata):
"""X25 record
@ivar address: the PSDN address
@type address: string
@see: RFC 1183"""
__slots__ = ['address']
def __init__(self, rdclass, rdtype, address):
super(X25, self).__init__(rdclass, rdtype)
if isinstance(address, text_type):
self.address = address.encode()
else:
self.address = address
def to_text(self, origin=None, relativize=True, **kw):
return '"%s"' % dns.rdata._escapify(self.address)
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
address = tok.get_string()
tok.get_eol()
return cls(rdclass, rdtype, address)
def to_wire(self, file, compress=None, origin=None):
l = len(self.address)
assert l < 256
file.write(struct.pack('!B', l))
file.write(self.address)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
l = wire[current]
current += 1
rdlen -= 1
if l != rdlen:
raise dns.exception.FormError
address = wire[current: current + l].unwrap()
return cls(rdclass, rdtype, address)

View File

@@ -0,0 +1,57 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""Class ANY (generic) rdata type classes."""
__all__ = [
'AFSDB',
'AVC',
'CAA',
'CDNSKEY',
'CDS',
'CERT',
'CNAME',
'CSYNC',
'DLV',
'DNAME',
'DNSKEY',
'DS',
'EUI48',
'EUI64',
'GPOS',
'HINFO',
'HIP',
'ISDN',
'LOC',
'MX',
'NS',
'NSEC',
'NSEC3',
'NSEC3PARAM',
'OPENPGPKEY',
'PTR',
'RP',
'RRSIG',
'RT',
'SOA',
'SPF',
'SSHFP',
'TLSA',
'TXT',
'URI',
'X25',
]

70
src/dns/rdtypes/CH/A.py Normal file
View File

@@ -0,0 +1,70 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.mxbase
import struct
class A(dns.rdtypes.mxbase.MXBase):
"""A record for Chaosnet
@ivar domain: the domain of the address
@type domain: dns.name.Name object
@ivar address: the 16-bit address
@type address: int"""
__slots__ = ['domain', 'address']
def __init__(self, rdclass, rdtype, address, domain):
super(A, self).__init__(rdclass, rdtype, address, domain)
self.domain = domain
self.address = address
def to_text(self, origin=None, relativize=True, **kw):
domain = self.domain.choose_relativity(origin, relativize)
return '%s %o' % (domain, self.address)
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
domain = tok.get_name()
address = tok.get_uint16(base=8)
domain = domain.choose_relativity(origin, relativize)
tok.get_eol()
return cls(rdclass, rdtype, address, domain)
def to_wire(self, file, compress=None, origin=None):
self.domain.to_wire(file, compress, origin)
pref = struct.pack("!H", self.address)
file.write(pref)
def to_digestable(self, origin=None):
return self.domain.to_digestable(origin) + \
struct.pack("!H", self.address)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
(domain, cused) = dns.name.from_wire(wire[: current + rdlen-2],
current)
current += cused
(address,) = struct.unpack('!H', wire[current: current + 2])
if cused+2 != rdlen:
raise dns.exception.FormError
if origin is not None:
domain = domain.relativize(origin)
return cls(rdclass, rdtype, address, domain)
def choose_relativity(self, origin=None, relativize=True):
self.domain = self.domain.choose_relativity(origin, relativize)

View File

@@ -0,0 +1,22 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""Class CH rdata type classes."""
__all__ = [
'A',
]

54
src/dns/rdtypes/IN/A.py Normal file
View File

@@ -0,0 +1,54 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.exception
import dns.ipv4
import dns.rdata
import dns.tokenizer
class A(dns.rdata.Rdata):
"""A record.
@ivar address: an IPv4 address
@type address: string (in the standard "dotted quad" format)"""
__slots__ = ['address']
def __init__(self, rdclass, rdtype, address):
super(A, self).__init__(rdclass, rdtype)
# check that it's OK
dns.ipv4.inet_aton(address)
self.address = address
def to_text(self, origin=None, relativize=True, **kw):
return self.address
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
address = tok.get_identifier()
tok.get_eol()
return cls(rdclass, rdtype, address)
def to_wire(self, file, compress=None, origin=None):
file.write(dns.ipv4.inet_aton(self.address))
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
address = dns.ipv4.inet_ntoa(wire[current: current + rdlen])
return cls(rdclass, rdtype, address)

View File

@@ -0,0 +1,55 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.exception
import dns.inet
import dns.rdata
import dns.tokenizer
class AAAA(dns.rdata.Rdata):
"""AAAA record.
@ivar address: an IPv6 address
@type address: string (in the standard IPv6 format)"""
__slots__ = ['address']
def __init__(self, rdclass, rdtype, address):
super(AAAA, self).__init__(rdclass, rdtype)
# check that it's OK
dns.inet.inet_pton(dns.inet.AF_INET6, address)
self.address = address
def to_text(self, origin=None, relativize=True, **kw):
return self.address
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
address = tok.get_identifier()
tok.get_eol()
return cls(rdclass, rdtype, address)
def to_wire(self, file, compress=None, origin=None):
file.write(dns.inet.inet_pton(dns.inet.AF_INET6, self.address))
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
address = dns.inet.inet_ntop(dns.inet.AF_INET6,
wire[current: current + rdlen])
return cls(rdclass, rdtype, address)

165
src/dns/rdtypes/IN/APL.py Normal file
View File

@@ -0,0 +1,165 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import binascii
import codecs
import struct
import dns.exception
import dns.inet
import dns.rdata
import dns.tokenizer
from dns._compat import xrange, maybe_chr
class APLItem(object):
"""An APL list item.
@ivar family: the address family (IANA address family registry)
@type family: int
@ivar negation: is this item negated?
@type negation: bool
@ivar address: the address
@type address: string
@ivar prefix: the prefix length
@type prefix: int
"""
__slots__ = ['family', 'negation', 'address', 'prefix']
def __init__(self, family, negation, address, prefix):
self.family = family
self.negation = negation
self.address = address
self.prefix = prefix
def __str__(self):
if self.negation:
return "!%d:%s/%s" % (self.family, self.address, self.prefix)
else:
return "%d:%s/%s" % (self.family, self.address, self.prefix)
def to_wire(self, file):
if self.family == 1:
address = dns.inet.inet_pton(dns.inet.AF_INET, self.address)
elif self.family == 2:
address = dns.inet.inet_pton(dns.inet.AF_INET6, self.address)
else:
address = binascii.unhexlify(self.address)
#
# Truncate least significant zero bytes.
#
last = 0
for i in xrange(len(address) - 1, -1, -1):
if address[i] != maybe_chr(0):
last = i + 1
break
address = address[0: last]
l = len(address)
assert l < 128
if self.negation:
l |= 0x80
header = struct.pack('!HBB', self.family, self.prefix, l)
file.write(header)
file.write(address)
class APL(dns.rdata.Rdata):
"""APL record.
@ivar items: a list of APL items
@type items: list of APL_Item
@see: RFC 3123"""
__slots__ = ['items']
def __init__(self, rdclass, rdtype, items):
super(APL, self).__init__(rdclass, rdtype)
self.items = items
def to_text(self, origin=None, relativize=True, **kw):
return ' '.join(map(str, self.items))
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
items = []
while 1:
token = tok.get().unescape()
if token.is_eol_or_eof():
break
item = token.value
if item[0] == '!':
negation = True
item = item[1:]
else:
negation = False
(family, rest) = item.split(':', 1)
family = int(family)
(address, prefix) = rest.split('/', 1)
prefix = int(prefix)
item = APLItem(family, negation, address, prefix)
items.append(item)
return cls(rdclass, rdtype, items)
def to_wire(self, file, compress=None, origin=None):
for item in self.items:
item.to_wire(file)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
items = []
while 1:
if rdlen == 0:
break
if rdlen < 4:
raise dns.exception.FormError
header = struct.unpack('!HBB', wire[current: current + 4])
afdlen = header[2]
if afdlen > 127:
negation = True
afdlen -= 128
else:
negation = False
current += 4
rdlen -= 4
if rdlen < afdlen:
raise dns.exception.FormError
address = wire[current: current + afdlen].unwrap()
l = len(address)
if header[0] == 1:
if l < 4:
address += b'\x00' * (4 - l)
address = dns.inet.inet_ntop(dns.inet.AF_INET, address)
elif header[0] == 2:
if l < 16:
address += b'\x00' * (16 - l)
address = dns.inet.inet_ntop(dns.inet.AF_INET6, address)
else:
#
# This isn't really right according to the RFC, but it
# seems better than throwing an exception
#
address = codecs.encode(address, 'hex_codec')
current += afdlen
rdlen -= afdlen
item = APLItem(header[0], negation, address, header[1])
items.append(item)
return cls(rdclass, rdtype, items)

View File

@@ -0,0 +1,61 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import base64
import dns.exception
class DHCID(dns.rdata.Rdata):
"""DHCID record
@ivar data: the data (the content of the RR is opaque as far as the
DNS is concerned)
@type data: string
@see: RFC 4701"""
__slots__ = ['data']
def __init__(self, rdclass, rdtype, data):
super(DHCID, self).__init__(rdclass, rdtype)
self.data = data
def to_text(self, origin=None, relativize=True, **kw):
return dns.rdata._base64ify(self.data)
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
chunks = []
while 1:
t = tok.get().unescape()
if t.is_eol_or_eof():
break
if not t.is_identifier():
raise dns.exception.SyntaxError
chunks.append(t.value.encode())
b64 = b''.join(chunks)
data = base64.b64decode(b64)
return cls(rdclass, rdtype, data)
def to_wire(self, file, compress=None, origin=None):
file.write(self.data)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
data = wire[current: current + rdlen].unwrap()
return cls(rdclass, rdtype, data)

View File

@@ -0,0 +1,150 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import base64
import dns.exception
import dns.inet
import dns.name
class IPSECKEY(dns.rdata.Rdata):
"""IPSECKEY record
@ivar precedence: the precedence for this key data
@type precedence: int
@ivar gateway_type: the gateway type
@type gateway_type: int
@ivar algorithm: the algorithm to use
@type algorithm: int
@ivar gateway: the public key
@type gateway: None, IPv4 address, IPV6 address, or domain name
@ivar key: the public key
@type key: string
@see: RFC 4025"""
__slots__ = ['precedence', 'gateway_type', 'algorithm', 'gateway', 'key']
def __init__(self, rdclass, rdtype, precedence, gateway_type, algorithm,
gateway, key):
super(IPSECKEY, self).__init__(rdclass, rdtype)
if gateway_type == 0:
if gateway != '.' and gateway is not None:
raise SyntaxError('invalid gateway for gateway type 0')
gateway = None
elif gateway_type == 1:
# check that it's OK
dns.inet.inet_pton(dns.inet.AF_INET, gateway)
elif gateway_type == 2:
# check that it's OK
dns.inet.inet_pton(dns.inet.AF_INET6, gateway)
elif gateway_type == 3:
pass
else:
raise SyntaxError(
'invalid IPSECKEY gateway type: %d' % gateway_type)
self.precedence = precedence
self.gateway_type = gateway_type
self.algorithm = algorithm
self.gateway = gateway
self.key = key
def to_text(self, origin=None, relativize=True, **kw):
if self.gateway_type == 0:
gateway = '.'
elif self.gateway_type == 1:
gateway = self.gateway
elif self.gateway_type == 2:
gateway = self.gateway
elif self.gateway_type == 3:
gateway = str(self.gateway.choose_relativity(origin, relativize))
else:
raise ValueError('invalid gateway type')
return '%d %d %d %s %s' % (self.precedence, self.gateway_type,
self.algorithm, gateway,
dns.rdata._base64ify(self.key))
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
precedence = tok.get_uint8()
gateway_type = tok.get_uint8()
algorithm = tok.get_uint8()
if gateway_type == 3:
gateway = tok.get_name().choose_relativity(origin, relativize)
else:
gateway = tok.get_string()
chunks = []
while 1:
t = tok.get().unescape()
if t.is_eol_or_eof():
break
if not t.is_identifier():
raise dns.exception.SyntaxError
chunks.append(t.value.encode())
b64 = b''.join(chunks)
key = base64.b64decode(b64)
return cls(rdclass, rdtype, precedence, gateway_type, algorithm,
gateway, key)
def to_wire(self, file, compress=None, origin=None):
header = struct.pack("!BBB", self.precedence, self.gateway_type,
self.algorithm)
file.write(header)
if self.gateway_type == 0:
pass
elif self.gateway_type == 1:
file.write(dns.inet.inet_pton(dns.inet.AF_INET, self.gateway))
elif self.gateway_type == 2:
file.write(dns.inet.inet_pton(dns.inet.AF_INET6, self.gateway))
elif self.gateway_type == 3:
self.gateway.to_wire(file, None, origin)
else:
raise ValueError('invalid gateway type')
file.write(self.key)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
if rdlen < 3:
raise dns.exception.FormError
header = struct.unpack('!BBB', wire[current: current + 3])
gateway_type = header[1]
current += 3
rdlen -= 3
if gateway_type == 0:
gateway = None
elif gateway_type == 1:
gateway = dns.inet.inet_ntop(dns.inet.AF_INET,
wire[current: current + 4])
current += 4
rdlen -= 4
elif gateway_type == 2:
gateway = dns.inet.inet_ntop(dns.inet.AF_INET6,
wire[current: current + 16])
current += 16
rdlen -= 16
elif gateway_type == 3:
(gateway, cused) = dns.name.from_wire(wire[: current + rdlen],
current)
current += cused
rdlen -= cused
else:
raise dns.exception.FormError('invalid IPSECKEY gateway type')
key = wire[current: current + rdlen].unwrap()
return cls(rdclass, rdtype, header[0], gateway_type, header[2],
gateway, key)

23
src/dns/rdtypes/IN/KX.py Normal file
View File

@@ -0,0 +1,23 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.mxbase
class KX(dns.rdtypes.mxbase.UncompressedMX):
"""KX record"""

127
src/dns/rdtypes/IN/NAPTR.py Normal file
View File

@@ -0,0 +1,127 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import dns.exception
import dns.name
import dns.rdata
from dns._compat import xrange, text_type
def _write_string(file, s):
l = len(s)
assert l < 256
file.write(struct.pack('!B', l))
file.write(s)
def _sanitize(value):
if isinstance(value, text_type):
return value.encode()
return value
class NAPTR(dns.rdata.Rdata):
"""NAPTR record
@ivar order: order
@type order: int
@ivar preference: preference
@type preference: int
@ivar flags: flags
@type flags: string
@ivar service: service
@type service: string
@ivar regexp: regular expression
@type regexp: string
@ivar replacement: replacement name
@type replacement: dns.name.Name object
@see: RFC 3403"""
__slots__ = ['order', 'preference', 'flags', 'service', 'regexp',
'replacement']
def __init__(self, rdclass, rdtype, order, preference, flags, service,
regexp, replacement):
super(NAPTR, self).__init__(rdclass, rdtype)
self.flags = _sanitize(flags)
self.service = _sanitize(service)
self.regexp = _sanitize(regexp)
self.order = order
self.preference = preference
self.replacement = replacement
def to_text(self, origin=None, relativize=True, **kw):
replacement = self.replacement.choose_relativity(origin, relativize)
return '%d %d "%s" "%s" "%s" %s' % \
(self.order, self.preference,
dns.rdata._escapify(self.flags),
dns.rdata._escapify(self.service),
dns.rdata._escapify(self.regexp),
replacement)
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
order = tok.get_uint16()
preference = tok.get_uint16()
flags = tok.get_string()
service = tok.get_string()
regexp = tok.get_string()
replacement = tok.get_name()
replacement = replacement.choose_relativity(origin, relativize)
tok.get_eol()
return cls(rdclass, rdtype, order, preference, flags, service,
regexp, replacement)
def to_wire(self, file, compress=None, origin=None):
two_ints = struct.pack("!HH", self.order, self.preference)
file.write(two_ints)
_write_string(file, self.flags)
_write_string(file, self.service)
_write_string(file, self.regexp)
self.replacement.to_wire(file, compress, origin)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
(order, preference) = struct.unpack('!HH', wire[current: current + 4])
current += 4
rdlen -= 4
strings = []
for i in xrange(3):
l = wire[current]
current += 1
rdlen -= 1
if l > rdlen or rdlen < 0:
raise dns.exception.FormError
s = wire[current: current + l].unwrap()
current += l
rdlen -= l
strings.append(s)
(replacement, cused) = dns.name.from_wire(wire[: current + rdlen],
current)
if cused != rdlen:
raise dns.exception.FormError
if origin is not None:
replacement = replacement.relativize(origin)
return cls(rdclass, rdtype, order, preference, strings[0], strings[1],
strings[2], replacement)
def choose_relativity(self, origin=None, relativize=True):
self.replacement = self.replacement.choose_relativity(origin,
relativize)

View File

@@ -0,0 +1,60 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import binascii
import dns.exception
import dns.rdata
import dns.tokenizer
class NSAP(dns.rdata.Rdata):
"""NSAP record.
@ivar address: a NASP
@type address: string
@see: RFC 1706"""
__slots__ = ['address']
def __init__(self, rdclass, rdtype, address):
super(NSAP, self).__init__(rdclass, rdtype)
self.address = address
def to_text(self, origin=None, relativize=True, **kw):
return "0x%s" % binascii.hexlify(self.address).decode()
@classmethod
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
address = tok.get_string()
tok.get_eol()
if address[0:2] != '0x':
raise dns.exception.SyntaxError('string does not start with 0x')
address = address[2:].replace('.', '')
if len(address) % 2 != 0:
raise dns.exception.SyntaxError('hexstring has odd length')
address = binascii.unhexlify(address.encode())
return cls(rdclass, rdtype, address)
def to_wire(self, file, compress=None, origin=None):
file.write(self.address)
@classmethod
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
address = wire[current: current + rdlen].unwrap()
return cls(rdclass, rdtype, address)

View File

@@ -0,0 +1,23 @@
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import dns.rdtypes.nsbase
class NSAP_PTR(dns.rdtypes.nsbase.UncompressedNS):
"""NSAP-PTR record"""

Some files were not shown because too many files have changed in this diff Show More